【问题标题】:Core Wars 8086 safe challenge (Safe uses addition on a memory address)Core Wars 8086 安全挑战(安全使用内存地址上的加法)
【发布时间】:2021-03-21 04:27:21
【问题描述】:

我正在参加保险箱比赛,我得到了这个保险箱:

start:
add     ds:0DEDh, ax
xor     cx, cx
loop    start

据我了解,cx 将在循环结束时为 0,并在下一次迭代时变为 FFFF。 我也知道 0xCCh 是一条会停止程序的非法指令。我怎样才能破解这个保险箱?

**编辑: 这里的目标是停止这个无限循环。循环没有停止项,我需要以某种方式使其停止使用逆向工程。 例如: 这是一个简单的保险箱

safe:
  mov     ax, ds:4D2h
  cmp     ax, 1000h
  jl      safe 

这是它的关键,使用逆向工程编写:

mov bx, 1000h
mov [4D2h], bx    
l:
    jmp l

safekey 的模拟在 Core Wars 8086 engine 内完成。 rules 如下,其中 safekey 都是战争中的幸存者:

幸存者不能在固定地址上加载,因为游戏 引擎每轮将它们加载到一个随机地址。那些节目 生成的必须是 COM 而不是 EXE,并且只包含 8086 指令。

每个幸存者都会收到一组自己的完整寄存器 (寄存器),其他幸存者无法访问。在 此外,每个幸存者都有一个 2048 字节的“个人”堆栈,即 其他幸存者也无法进入。

在运行第一轮游戏之前,游戏引擎 将 arena 中的所有字节初始化为值 0CCh(注意:这 字节值是“不受支持”的指令 - 详情如下)。那么发动机 将每个幸存者加载到竞技场内存中的随机位置,即 - 完全按原样复制幸存者文件的内容。这 两个幸存者之间的距离,以及两个幸存者之间的距离 幸存者和竞技场边缘,保证至少1024 字节。每个幸存者的代码最大为 512 字节。

在第一轮之前,游戏引擎初始化寄存器(的 每个幸存者)到以下值:

  • BX、CX、DX、SI、DI、BP - 重置。
  • 标志 - 重置。
  • AX, IP - 初始幸存者的位置,游戏引擎加载幸存者的竞技场中的随机偏移量。
  • CS、DS - 所有幸存者共有的竞技场部分。
  • ES - 同一组的幸存者共享内存的段(段)(请参阅高级技术)。
  • SS - 幸存者个人堆栈的开始部分。
  • SP - 偏移幸存者个人堆栈的开始。

此时游戏以回合开始,每一回合运行游戏引擎运行每个幸存者的下一条指令,直到结束 游戏结束:在 200,000 轮之后,或者当一个幸存者仍然存在时 在竞技场。幸存者在每轮比赛中的顺序 在游戏开始时随机确定,并且不 期间发生变化。

在以下情况下,幸存者将被取消资格:

  • 运行非法指令(例如:字节 060h,不翻译成任何汇编指令)。
  • 游戏引擎运行“不支持”指令(例如:“INT 021h”)。游戏引擎阻止尝试启动的运行指令 与操作系统或计算机硬件的直接通信。 尝试访问不在竞技场范围内的内存, 并且不在幸存者的“个人”堆栈范围内。
  • 攻击其他幸存者是通过在竞技场内存中写入有关他们的代码的信息来完成的(为了让他们执行其中一项 以上三项行动),并因此取消他们的资格。早些时候, 因此,必须找到它们的藏身之处:)

【问题讨论】:

  • 内存中的代码在哪里? add 可以修改代码字节吗?这里的目标到底是什么,你控制什么参数?我假设您可以选择寄存器内容或其他内容,并且您正在尝试安排执行以退出循环? “保险箱竞争”并不是一个非常知名的术语。
  • 对于我们这些不知道此类比赛如何运作的人,您能否更清楚地解释您要完成的工作? “破解保险箱”意味着什么?
  • “钥匙”可以用不同的指令覆盖保险柜的机器码吗?这个保险箱对我来说没有多大意义。循环结构仅取决于寄存器(特别是 CX),而不是内存内容。 add ds:0DEDh, ax 只在ds:0DEDh 更改内存,所以除非这恰好与代码重叠,否则我认为没有办法通过将任何东西放在内存中的任何位置else 来解锁自己.此外,它所做的修改取决于安全“线程”的 AX 中的内容。你能以某种方式控制寄存器吗?
  • 我唯一的猜测是,也许你可以在进入循环之前设置ds。在这种情况下,根据安全代码与 16 字节边界对齐的方式,您可能会得到 add 来修改其代码。但是如果没有对“规则”的更精确和详细的解释,我认为这是无法回答的。
  • @MichaelPetch:好的是有道理的。如果 OP 花时间在那个详细程度进行解释(即使没有关于解决方案应该如何工作的部分),那么问题就不会首先解决。或者此时,可以在编辑后重新打开。但仅仅说“AX 是随机的”根本没有帮助。

标签: assembly nasm reverse-engineering x86-16 corewars


【解决方案1】:

虽然AX有一个随机值,但它确实有意义。那意思是:

  • AX, IP - 初始幸存者的位置,游戏引擎加载幸存者的竞技场中的随机偏移量。

AX=IP 这是 safe 在内存中加载的指令指针。我们所要做的就是用强制safe 退出(死)的东西更新循环中的第一条指令。这可以通过根据文档将字节值 0x601 写入该内存地址来完成。由于 safe 的循环也是程序的开始,我们只需要检索 safeAX 值并用它来覆盖它内存地址。

如果他们只是将 safeAX 写入内存地址 0x0DED 但他们不断将 AX 添加到内存中的前一个 WORD(16 位值)。这意味着为了弄清楚 safeAX 值是多少,我们需要读取它两次,然后从读取的第二个值中减去读取的第一个值。我们还必须以我们知道 safe 在两次读取之间准确更新一次的方式进行 2 次读取。 Core Ware引擎的规则说:

此时游戏以回合开始,每一回合运行游戏引擎运行每个幸存者的下一条指令,直到游戏结束:在 200,000 回合之后,或者当一个幸存者留在竞技场时。幸存者在每一轮中的出场顺序是在游戏开始时随机确定的,在游戏过程中不会改变。

这意味着每一轮指令都由 safekey 执行。由于 safe 的循环是 3 条指令:

start:
    add     ds:0DEDh, ax ; Instruction 1
    xor     cx, cx       ; Instruction 2
    loop    start        ; Instruction 3

我们保证每 4 条指令在 [0x0DED] 读取 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

在 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

脚注

  • 18086 不支持的任何操作码都会终止幸存者,这包括 0x60、0x66、0x67 等。 0xcc (INT3)、0xce (INTO) 和 0xf1 (INT1) 等有效的 8086 单字节操作码也将退出,因为它们是 Core Wars 引擎中不受支持的陷阱。调用 Core Wars 引擎不支持的软件中断(如 DOS INT 21h)也会导致幸存者失败。

【讨论】:

  • 看到您提供了 example 保险箱,而不是您为其提供解决方案的 target 保险箱,我感到有些惊讶。所有这些“保险箱”是否都只有 3 条指令?
  • 是的,“1024 字节。512 座房子”是我从谷歌翻译中没有得到的。
  • @SepRoland :直到我读到这个 ​​power point 演示文稿(第 11 页),我才明白那个翻译:recon.cx/2014/slides/…。稍后我会在 [corewars] 标签中添加信息和链接。我注意到我们还有其他问题,包括这个保险箱:stackoverflow.com/questions/59512527/…
  • @SepRoland 我刚刚意识到你指的是什么。当我将 cmets 放在它们上时,我没有注意到我复制并粘贴了错误的循环。糟糕,现在已修复。
  • @snatchysquid :纠正任何未定义的操作码都会使其失败(0x66 和 0x60,甚至 0x67 等)。我已经通过在 Core Wars 中对其进行测试来验证这一点。同样,核心战争引擎不允许的任何中断调用也会杀死幸存者。如果你将int 21h 输入你的幸存者,它也会结束。
【解决方案2】:

这里的目标是停止这个无限循环。循环没有停止项,我需要以某种方式使用逆向工程使其停止

不清楚你可以不能做什么。以下是只需要更改单个字节的解决方案。

xor cx, cx 到无害的mov cx, cx 将不再重置循环计数器,因此循环将在一段时间后结束(取决于我们不知道的CX 的初始值)。
mov cx, cx 的操作码是 89h。我们不需要更改 modr/m 字节,因为两条指令的值相同。

mov byte [cs:start+4], 89h

验证汇编器是否不包含其他多余的 DS: 段覆盖前缀可能很有用,因为如果是这种情况,您将不得不编写 mov byte [cs:start+5], 89h

【讨论】:

  • @MichaelPetch AX 是内存中保险箱的地址是关键缺失的信息。在连续读取之间必须等待几个指令是一种给定的。我相信你的解决方案会成功。干得好!
猜你喜欢
  • 1970-01-01
  • 2022-06-14
  • 1970-01-01
  • 2012-06-05
  • 2017-10-28
  • 1970-01-01
  • 2017-03-23
  • 2020-08-29
  • 2011-09-08
相关资源
最近更新 更多