OllyDBG 保存的可执行文件崩溃

逆向工程 ollydbg 修补
2021-07-09 20:30:42

我对这种事情有点陌生,所以我希望有人可以帮助我。我目前正在尝试了解大多数恶意软件如何感染其他文件/修改 exe。使用 Visual Studio 2017 我制作了一个 exe(使用 C++),它只做两件事:打印“注入不起作用!” 然后运行“暂停> NUL”。我在 x86 Release 模式下编译它。然后我制作了第二个 exe,它只打印“注射有效”。我的主要目标是将代码注入第一个/目标 exe,以便它先运行第二个 exe,然后再运行它自己。所以注入后的最终输出应该是这样的:“注入有效。注入无效!”。我的想法是使用 OllyDBG。我打开目标exe,在代码末尾的代码洞中加入如下代码:

ASCII "inject.exe"
PUSH 1
PUSH 008B1F7C               (Address of ASCII "inject.exe")
Call WinExec
Call __security_init_cookie (This is the assembly code at the entrypoint which I overwrote with a JMP instruction to this codecave)
JMP 008B1572 (JMP back to next instruction after entrypoint)

入口点如下所示:

JMP 008B1F87 (JMP to codecave)

这段代码应该执行inject.exe,这是我制作的第二个exe(这个exe与目标exe位于同一文件夹中)。当我在 OllyDBG 中运行它时它可以工作并且它给了我想要的输出但是一旦我将它保存到一个 exe(复制到可执行文件 -> 所有修改 -> 全部复制 -> 另存为 InjectionTestPatched.exe)它不会运行并在打印任何内容之前崩溃。我还在执行后检查了 %errorlevel%,它是 -1073741819 而不是 0。我真的不明白为什么代码在 OllyDBG 中有效,但当我将它保存为 exe 时却没有。

有人知道这里发生了什么吗?(请原谅我的英语不好,因为我不是英语母语者)

编辑:我想我知道为什么它会这样,但我不知道如何解决它。当我调试补丁 EXE 时,每个地址似乎都是正确的,但 ASCII 的地址却不是。所有 JMP/CALL 地址都会根据偏移量进行相应调整,但 ASCII 的地址保持不变(PUSH 008B1F7C 不会改变)。ASLR 会不会是我问题的根源?如果是的话,我怎么能绕过这个......

奥莉DBG

这里红色的地址应该指向ASCII,但它和以前一样(在exe生成后地址发生了变化)。我不明白的是,JMP 地址更改为正确的地址,但 PUSH 未更改。为什么会发生这种情况,我该如何解决?(EDIT2:问题解决)

EDIT2:所以我想出了一种方法,通过将 EIP 加载到 EAX 并将其子化,每次都正确地将 ASCII 地址推送到堆栈上。这似乎有效,因为当我重新加载代码时,ASCII 地址总是在堆栈中。但是现在我遇到了下一个问题,即重新加载后 WinExec 似乎不再工作了。OllyDBG 甚至不再识别这条指令,因为它将它显示为 4 个单独的 DB 行,而不是 CALL WinExec。我怎么能解决这个问题?

EDIT3:问题解决了!尽管地址始终相同,但由于 ASLR,WinExec 的调用似乎已更改。我通过将静态地址加载到 EAX 来解决它,然后使用 CALL EAX 我可以调用一个不受 ASLR 影响的静态地址。我的注射现在起作用了。

1个回答

所以我终于解决了我的问题。这里有一个关于我是如何做到的快速解释:

最初的问题是我使用了“固定”地址,PUSH意思是它总是推送相同的地址(应该是 ASCII 的地址)。但是 ASLR 在创建 EXE 时随机化基地址,这意味着地址变得无效,因为它现在指向与以前完全不同的位置。解决方法是更“相对”而不是绝对地确定地址。

主要思想是以某种方式获取当前地址 ( EIP) 并从中减去一个特定数字,以便现在它指向 ASCII。这可以通过创建一个被调用的函数来实现。每次CALL执行a时,下一条指令的地址都会被压入堆栈。在这个函数中,地址被保存在EAXwith 中POP,然后被推回,以便RET仍然知道从哪里返回。

CALL在字符串后面添加了一个来检索地址并10h从中减去现在我有了 ASCII 的地址,EAX然后遵循与以前相同的代码,其中WinExec调用的唯一区别是现在EAX被推入堆栈而不是“绝对”地址。在调用之前,WinExec我保存了WinExecin的地址EAX(因为 WinExec 的地址即使使用 ASLR 也保持不变,而 ASLR 的问题是地址将被修改,因此它将不再对应WinExec)。然后我只是做了一个CALL EAX运行WinExec,其余的都是一样的。

这是完整的代码:

Entrypoint: JMP <address of the first CALL in injected code>

POP EAX                               (Puts address of next instruction into EAX)
PUSH EAX                              (Copies address back to the stack so that RET knows where to go)
RET

ASCII "inject.exe"
CALL <address of the routine above>   (puts address of next instruction into EAX)
SUB EAX,0x10                          (SUB EAX,<this number depends on the size of the executable name. Just subtract the address of this instruction with the address of the ASCII>)
PUSH 1
PUSH EAX
PUSH WinExec                          (Puts address of WinExec onto stack so that the address won't change because of ASLR)
POP EAX
CALL EAX                              (execute WinExec)
CALL __security_init_cookie           (replace this instruction with the instruction that will be overwritten by the JMP instruction to this code cave. In my case it's this call)
JMP <address of second Instruction at Entrypoint>