JMP到绝对地址(操作码)

Chr*_*ini 6 x86 assembly executable masm opcode

我正在尝试编写一个exe包装器/保护器作为一种学习更多关于汇编程序,c ++以及PE文件如何工作的方法.我现在已经开始工作,所以包含EP的部分与一个密钥进行异或,并创建一个包含我的解密代码的新部分.一切都很好,除非我在解密后尝试JMP到原始EP.

基本上我这样做:

DWORD originalEntryPoint = optionalHeader->AddressOfEntryPoint;
// -- snip -- //
    crypted.put(0xE9);
 crypted.write((char*)&orginalEntryPoint, sizeof(DWORD)); 
Run Code Online (Sandbox Code Playgroud)

但是,它不是跳到入口点,而是显示此代码反汇编为:

00404030   .-E9 00100000    JMP 00405035 ; should be 00401000 =[
Run Code Online (Sandbox Code Playgroud)

当我尝试手动更改它时,新的操作码显示为

00404030    -E9 CBCFFFFF    JMP crypted.00401000
Run Code Online (Sandbox Code Playgroud)

0xCBCFFFFF来自哪里?我如何从C++端生成它?

Bar*_*cik 17

你可以使用:

push DESTINATION_VA
ret
Run Code Online (Sandbox Code Playgroud)

要么

mov eax,DESTINATION_VA
jmp eax
Run Code Online (Sandbox Code Playgroud)

这个和下一个最多16个__CODE__指令返回调用树高于这个深度将错误预测,除非它们被更深的调用深度推离返回地址预测器堆栈.(当前CPU通常具有16个条目的预测器堆栈).


相对__CODE__ __CODE__编码使用如下:

CURRENT_RVA: jmp (DESTINATION_RVA - CURRENT_RVA - 5 [sizeof(E9 xx xx xx xx)])
Run Code Online (Sandbox Code Playgroud)

push + ret是最小的解决方案,如果你有VA地址并且图像没有重新定位,但它仍然是6个字节,所以它比直接大__CODE__.

如果你不能使用普通直接,那么注册间接可能是最有效的__CODE__.

  • [我对此答案的编辑](https://stackoverflow.com/revisions/2049606/2)解决了“push”/“ret”的严重性能问题,但不幸的是海报将其回滚。如果您关心现代 x86 上的性能,请使用“mov eax, dst”/“jmp eax”,而不是使用与“call”不匹配的“ret”来不平衡返回地址预测器(https:// agner.org/optimize/)。和/或参见[在 x86 机器代码中调用绝对指针](//stackoverflow.com/q/19552158)。**`mov`/`jmp` 与 `push`/`ret` 相比会多花费 1 个字节,并且如果您从目标 `ret` 则可以避免未来的分支错误预测** (3认同)

Chr*_*isW 6

我认为这E9是一个相对跳转的操作码:它的操作数指定要跳过的相对距离,加上或减去下一条指令的开头.

如果希望操作数指定绝对地址,则需要不同的操作码.

  • 这并没有回答这个问题,只是确认了 OP 的答案是错误的。 (2认同)

小智 5

绝对间接跳转的操作码是FF + 4byte地址。这最常用于存储在数据中的地址的跳转表。

绝对地址在未加载到预期地址时确实需要重定位,因此通常首选相对地址。相对跳转的代码也小2个字节。

英特尔优化手册指出,CPU预期呼叫和重新使用将成对使用,因此答案2中建议的没有呼叫的重新使用会导致他们所谓的“性能下降”。

另外,如果代码未加载到编译器假定的相同地址,则ret可能会使程序崩溃。计算相对地址会更安全。

  • 也许您是说FF 25 4byte .. FF 4个字节根本没有意义。 (3认同)