JMP到绝对地址(操作码)

时间:2009-10-09 21:32:10

标签: assembly x86 executable masm opcode

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

基本上我这样做:

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

但是,ollydbg没有跳转到入口点,而是显示此代码反汇编为:

00404030   .-E9 00100000    JMP 00405035 ; should be 00401000 =[

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

00404030    -E9 CBCFFFFF    JMP crypted.00401000

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

3 个答案:

答案 0 :(得分:18)

你可以使用:

mov eax,DESTINATION_VA
jmp eax                ; pick any register the destination doesn't care about

push DESTINATION_VA
ret                    ; not recommended for performance

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


相对E9 jmp编码的使用方式如下:

CURRENT_RVA: jmp (DESTINATION_RVA - CURRENT_RVA - 5 [sizeof(E9 xx xx xx xx)])

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

如果您不能使用普通直接jmp,则注册间接可能是最有效的。

答案 1 :(得分:5)

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

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

答案 2 :(得分:4)

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

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

英特尔优化手册指出cpu期望call和ret成对使用,因此在答案2中建议没有调用的ret将导致他们所谓的“性能损失”。

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