DD3*_*314 2 windows x86 assembly memory-address codecave
这是我的第一篇文章,我是组装和调试的新手,所以请耐心等待。
我试图将一些代码(对 MessageBoxA 的一个小调用)注入到 Windows 7 记事本可执行文件中。但是,我遇到了地址问题。首先,我在 OllyDbg 中打开 exe,然后我转到包含“notepad.pdb” ASCII 文本的行。然后我在它下面放了一个 ASCII 字符串(例如,“INJECTED NOTEPAD”)。接下来,在下面,我输入了这个汇编代码:
PUSH 0
PUSH address_of_ASCII_string ; In this case, 00A6B668C
PUSH address_of_ASCII_string ; In this case, 00A6B668C
PUSH 0
CALL MessageBoxA
Run Code Online (Sandbox Code Playgroud)
接下来,我转到程序中的第一行代码(只需右键单击并按Go to Origin(或直接按数字键盘上的*))然后我用JMP指令替换第一行到第一个的地址在我注入的代码中按 0。然后,我将替换的指令放在注入代码的末尾。在那之后,我在跳转到我注入的代码的 JMP 指令之后的代码行中放置了一条 JMP 指令(是的,我刚刚描述了一个 codecave 或某种类型)。当我运行它时一切正常。但是,当我将修改后的代码保存到新的可执行文件中并再次使用 OllyDbg 运行它时,它不起作用。当我尝试引用我输入的 ASCII 字符串时,地址完全错误。示例如下图所示:
如您所见,我将字符串压入堆栈,但是当我再次将修改后的程序重新加载到调试器中时,字符串的地址发生了变化,但我的代码没有变化。因此,当我调用 MessageBoxA 函数时,它会出错,因为我为 Text 和 Caption 参数加载了错误的地址。我该如何解决?
您遇到的是ASLR的影响。简而言之,这意味着在操作系统(在此示例中为 Windows)上运行的可执行文件在多次执行中不会具有相同的基地址。
因此,有几种方法可以将您的代码注入另一个二进制文件中,我将在这里解决 2 并且我还将猜测(在阅读评论后)您正在尝试修补磁盘上的二进制文件。
修补磁盘上的二进制文件:
一种。解决此问题的第一种方法是从PE 中剥离重定位表。我绝对不会推荐这种方式,因为它可能会在将来导致可执行文件崩溃,以防它没有被加载到它喜欢的基地址(OptionalHeader.ImageBase)中一个偏移量。
假设您有一个0x600
与映像基址偏移的字符串,以防可执行文件映射到其首选基址(通常为0x00400000
),该字符串将保存在 address 处0x00400000
。您的编译器链接文件的方式将使用0x00400000 + 0x600
so的地址0x00400600
。那么当一个可执行文件被加载到不同的基地址时会发生什么?在这种情况下,windows 可执行加载器会将基地址的偏移量(实际上是映射的基地址减去首选基地址)添加到每个重定位条目(上面的示例将需要一个)。因此,如果可执行文件将被加载到0x00500000
加载器中,则加载器将添加0x00100000
到导致0x00500600
.
湾 第二种方法是向二进制文件添加一个重定位条目。这样,与 (a) 中描述的方式相反,您不会损害二进制文件,只会添加一个重定位条目,在加载可执行文件时,加载程序会将地址更改为正确的地址(如果您添加了正确的地址)入口)。
C。编写位置无关代码并使用此代码修补二进制文件。例子:
sub esp, 10*4
mov byte [esp], 0x68 ; 'h'
mov byte [esp + 1], 0x69 ; 'i'
mov byte [esp + 2], 0x00 ; null byte
mov byte [esp + 3], 0x79 ; 'y'
mov byte [esp + 4], 0x6f ; 'o'
mov byte [esp + 5], 0x75 ; 'u'
mov byte [esp + 6], 0x00 ; null byte
mov eax, esp
push 0
push eax
add eax, 3
push eax
push 0
call [MessageBoxA]
Run Code Online (Sandbox Code Playgroud)
这段代码会将需要的 2 个字符串写入堆栈并将指针推送到堆栈上的字符串,这样代码就不需要任何重定位,因为它是一段独立于 Poisiton 的代码。
修补内存中的二进制文件:
一种。位置无关代码(见上文)
湾 使用VirtualAllocEx,此 API 将在调用它后返回一个您可以写入的地址,这样您只需在内存中拥有映射地址并知道在哪里以及如何注入您的代码。