我有一个程序的共享对象,我需要将指令从'jne / je'更改为jmp。我试图环顾四周,例如关于如何使用LD_PRELOAD进行此操作或无济于事。
答案 0 :(得分:5)
这在技术上是否可行取决于以下几点:
jne
/ je
说明有不同的长度:
0x74
/ 0x75
)加上有符号的8位偏移量(即总共两个字节)0xf0
加0x84
/ 0x85
,)加上有符号的32位偏移(即总共6个字节)与jmp
指令相同(在64位模式下,总共五个不同的编码在2到9个字节之间,具体取决于jmp
是相对还是绝对)。
这意味着,如果jne
的目标“足够接近”以选择与大小兼容的操作码,则只能将je
/ jmp
替换为jmp
。对于32位相对jne
/ je
,您通常可以替换,但对于近似(8位)通常不可能,因为在+/-内不会有“自由指令空间” 128字节。
如果您有“兼容的机会”wrt。根据指令的大小,你已经完成了两个你已经提到的任务:
如何做到这一点取决于您的操作系统;在类似于UN * X的情况下,您可以尝试使用LD_PRELOAD
加载自己的自定义库,而不是dlopen()
要修改的库,然后{{ 1}}找到例程,然后替换代码,但永远不要调用dlsym()
以确保修改后的副本保留(即未卸载)而不是原来(稍后通过动态链接)访问lib试。
系统管理员通常有办法缓解(甚至禁用)dlclose()
(对于以root身份运行的setuid可执行文件,它总是被忽略),因为它对系统完整性/安全性有明显的影响。因此,是否可以使用该技术取决于您具体的设置。
如果满足以上所有条件,那么就像:
LD_PRELOAD
可能会这样做。如上所述,除了检查要修补的地址上的指令操作是否兼容之外,此代码中没有验证/验证。
如上所述,主要工作是安全地确定您需要应用挂钩的位置/方式。
正如有人建议的那样,关于修补二进制库而不是正在运行(加载)的库的注释:关键问题是:int patch_je_jne(void *instr, void *tgt_address)
{
char *je_jne = (char*)instr;
char *iaddr = je_jne;
switch (je_jne[0]) {
case JE_8BIT_OP:
case JNE_8BIT_OP:
iaddr += 2;
tgt_address -= iaddr; /* adjust pc-relative */
if ((char*)tgt_address > (char*)0xFF)
return CANNOT_PATCH_LARGE_OFFSETS;
je_jne[1] = (char)tgt_address;
je_jne[0] = JMP_OP;
return PATCH_SUCCESS;
case 0xF0: /* marker for a 32bit-relative JE/JNE */
if (je_jne[1] != JE_32BIT_OP && je_jne[1] != JNE_32BIT_OP))
break;
iaddr += 6;
tgt_address -= iaddr; /* adjust pc-relative */
*((char**)(je_jne + 2)) = tgt_address;
je_jne[1] = JMP_32BIT_OP;
return PATCH_SUCCESS;
}
return CANNOT_PATCH_THIS_INSTR;
}
的目标是什么,你能否提前知道它?
在二进制修补中,您需要确定在修改二进制文件时,这与操作系统行为(如ASLR(地址空间布局随机化))一起难以实现 - 您需要一个预先知道的,不变的目标地址您的可执行文件,但通常只有 relative jmp
可用,即事先不知道由于ASLR而最终会导致哪些代码位置。这一切都取决于您希望jmp
转到的 。
答案 1 :(得分:0)
也许你可以只是Hex编辑你的对象?使用objdump获取正确的偏移量和操作码,然后编辑文件。
答案 2 :(得分:0)
添加以上答案,了解如何自动或多或少地更新二进制文件中的指令。
如果您知道指令在二进制文件的一个版本中的位置,您可以反汇编它周围的一大块代码(可能包含一些将控制转移到那里或从那里转移控制的代码?),并保存该反汇编。从反汇编及其jmp / call指令的立即操作数中删除指令地址,因为这些地址可能会随版本而变化。
同样反汇编新版本,找到最适合您之前拆解的地方并修补它。为了匹配,您可以使用一些差异工具。
当然,这只有在感兴趣的指令周围的代码不会改变并且是唯一的时才会起作用。
二进制文件中也可能需要更新校验和。 OTOH,如果你是管理员,你可以在内存中修补它。