nit*_*t17 1 assembly reverse-engineering nasm x86-16 corewars
我正在做一个保险箱比赛,我得到了这个保险箱:
start:
add ds:0DEDh, ax
xor cx, cx
loop start
Run Code Online (Sandbox Code Playgroud)
根据我的理解,cx 在循环结束时将为 0,并在下一次迭代时更改为 FFFF。我也知道 0xCCh 是会停止程序的非法指令。我怎样才能破解这个保险箱?
**编辑:这里的目标是停止这个无限循环。循环没有停止项,我需要以某种方式让它停止使用逆向工程。例如:这是一个简单的保险箱
safe:
mov ax, ds:4D2h
cmp ax, 1000h
jl safe
Run Code Online (Sandbox Code Playgroud)
这是它的关键,使用逆向工程编写:
mov bx, 1000h
mov [4D2h], bx
l:
jmp l
Run Code Online (Sandbox Code Playgroud)
这种保险箱和钥匙的模拟是在Core Wars 8086 引擎内完成的。该规则是如下,其中既安全和关键是在战争中的幸存者:
幸存者不能在固定地址上加载,因为游戏引擎每回合都会将它们加载到一个随机地址。生成的程序必须是 COM 而不是 EXE,并且只包含 8086 条指令。
每个幸存者收到一组自己的完整寄存器(寄存器),其他幸存者无法访问。此外,每个幸存者都有一个 2048 字节的“个人”堆栈,其他幸存者也无法访问。
在运行第一轮游戏之前,游戏引擎将竞技场中的所有字节初始化为值 0CCh(注意:此字节值是“不支持”的指令 - 详情如下)。然后引擎将每个幸存者加载到竞技场内存中的随机位置,即 - 完全按原样复制幸存者文件的内容。两个幸存者之间的距离,以及幸存者与竞技场边缘之间的距离,保证至少为 1024 字节。每个幸存者的代码最多有 512 个字节。
在第一轮之前,游戏引擎将(每个幸存者的)寄存器初始化为以下值:
- BX、CX、DX、SI、DI、BP - 重置。
- 标志 - 重置。
- AX, IP - 初始幸存者的位置,游戏引擎将幸存者加载到的竞技场中的随机偏移量。
- CS, DS - 所有幸存者共有的竞技场部分。
- ES - 一个段(segment),用于由同一组的幸存者共享的内存(参见高级技术)。
- SS - 幸存者个人堆栈的开始部分。
- SP - Offset 幸存者个人堆栈的开始。
此时游戏以回合开始,每一回合运行游戏引擎,运行每个幸存者的下一条指令,直到游戏结束:200,000 回合后,或者当一个幸存者留在竞技场时。幸存者在每一轮的比赛顺序是在游戏开始时随机确定的,在游戏中不会改变。
在以下情况下,幸存者将被取消资格:
- 运行非法指令(例如:未转换为任何汇编指令的字节 060h)。
- 由游戏引擎运行“不受支持”的指令(例如:“INT 021h”)。游戏引擎会阻止运行指令,尝试启动与操作系统或计算机硬件的直接通信。尝试访问不在竞技场范围内的内存,也不在幸存者的“个人”堆栈范围内。
- 攻击其他幸存者是通过在竞技场内存中写入关于他们的代码的信息来完成的(为了让他们执行上述三个动作之一),并因此取消他们的资格。因此,早些时候,人们必须找到他们藏身的地方:)
虽然AX有一个随机值,但它确实有意义。那意思是:
- AX, IP - 初始幸存者的位置,游戏引擎将幸存者加载到的竞技场中的随机偏移量。
AX = IP这是保险箱在内存中加载的指令指针。我们所要做的就是更新循环中的第一条指令,以强制保险箱退出(死亡)。这可以通过根据文档将字节值 0x60 1写入该内存地址来完成。由于safe的循环也是程序的开始,我们只需要检索safe的AX值并使用它来覆盖该内存地址。
如果他们只是将AX写入内存地址 0x0DED 但他们一直将AX添加到内存中的前一个 WORD(16 位值),那么获取safe的AX将是微不足道的。这意味着为了弄清楚safe的AX值是多少,我们需要读取它两次,并从读取的第二个值中减去读取的第一个值。我们还必须以这样一种方式进行 2 次读取,即我们知道安全在两次读取之间只更新了一次。Core Ware 引擎的规则说:
此时游戏以回合开始,每一回合运行游戏引擎,运行每个幸存者的下一条指令,直到游戏结束:200,000 回合后,或者当一个幸存者留在竞技场时。幸存者在每一轮的比赛顺序是在游戏开始时随机确定的,在游戏中不会改变。
这意味着每一轮一条指令都由安全和密钥执行。由于safe的循环是 3 条指令:
start:
add ds:0DEDh, ax ; Instruction 1
xor cx, cx ; Instruction 2
loop start ; Instruction 3
Run Code Online (Sandbox Code Playgroud)
我们保证[0x0DED]每 4 条指令读取 2 个不同的值。现在我们知道了这一点,它变得相对容易。我们可以通过实现secondreadAX - firstreadAX与(-firstreadAX) + secondreadAX. 考虑到这一点,执行 2 读取四个指令并使用 0x60更新safe代码的开始(及其循环的开始)的解决方案可能看起来像(在 NASM 语法中):
; Assemble with:
; nasm -f bin key.asm -o key
start:
mov bx, [0x0DED] ; Get the WORD from [0x0DED]
neg bx ; Negate it. BX=(-firstreadAX)
nop ; Need to wait at least one more instruction (3 total)
; before trying to read 0x0DED again
add bx, [0x0DED] ; Add the current WORD value at [0x0DED] with BX
; safeAX = -BX+[0x0DED] = [0x0DED]-BX
; or safeAX=(-firstreadAX)+secondreadAX = secondreadAX-firstreadAX
mov byte [bx], 0x60
; Overwrite the first instruction of safe with 0x60
; to terminate the safe.
jmp $ ; Infinite loop
Run Code Online (Sandbox Code Playgroud)
在 TASM/MASM(v6.00+)/JWASM 中,代码如下所示:
.model tiny
.code
start:
mov bx,[ds:0DEDh]; Get the WORD from [0DEDh]
neg bx ; Negate it. BX=(-firstreadAX)
nop ; Need to wait at least one more instruction (3 total)
; before trying to read 0DEDh again
add bx,[ds:0DEDh]; Add the current WORD value at [0DEDh] with BX
; safeAX=-BX+[0DEDh]=[0DEDh]-BX
; or safeAX=(-firstreadAX)+secondreadAX = secondreadAX-firstreadAX
mov byte ptr [bx], 060h
; Overwrite the first instruction of safe with 060h
; to terminate the safe.
jmp $ ; Infinite loop
END
Run Code Online (Sandbox Code Playgroud)
INT3)、0xce ( INTO) 和 0xf1 ( INT1) 也将退出,因为它们是 Core Wars 引擎中不受支持的陷阱。调用 Core Wars 引擎不支持的软件中断(如 DOS INT 21h)也会导致 Survivor 失败。