64位程序相对寻址访问全局变量的汇编都是
opcode ModR/M disp32 (紧跟着可能还有imm32)的形式 (rip+disp32相对寻址方式) ModR/M=00xxx101 Mod=00 r/m=101
可以用反汇编引擎找出来
比如
0xfffff80004173cd7 4c 8b 05 42 73 f5 ff mov r8, qword ptr ds:[0xFFFFF800040CB020]
ModR/M=0x05 r/m=101 表示 rip+disp32 内存相对寻址 r/m=rip+len+disp32
这种相对寻址的指令长度至少有 1 (opcode)+1(ModR/M)+4(disp32)=6 字节
可以放得下一个5字节的e9 jmp 跳到Label的位置
(在下方)
然后把ModR/M 的(r/m)改成0(rax)就变成 寄存器方式寻址 mov r8, qword ptr ds:[rax]
如果rax已经被使用 可以把r/m改成1(rcx) mov r8, qword ptr ds:[rcx]
然后原来指令后面的四字节disp32去掉 就变成 opcode ModR/M 形式
4c 8b 05 42 73 f5 ff(mov r8, qword ptr ds:[0xFFFFF800040CB020]) ->4c 8b 00 (mov r8, qword ptr ds:[rax])
如果另一个参数是rax寄存器则改为: 4c 8b 05 42 73 f5 ff(mov r8, qword ptr ds:[0xFFFFF800040CB020]) ->4c 8b 01 (mov r8, qword ptr ds:[rcx])
前后再加上一段代码就变成
Label:
push rax //最后的ret指令的返回地址
push rax //保存rax寄存器
mov rax, 0xFFFFF800040CB020 //原来指令需要读取或者写入的内存地址
mov r8, qword ptr ds:[rax] //相当于执行原来的指令 mov r8, qword ptr ds:[0xFFFFF800040CB020]
mov rax, 0xFFFFF80004173CDE //跳回下一条指令 rip+len的地址
mov qword ptr ss:[rsp+0x08], rax
pop rax//恢复rax的值
ret //跳回 mov r8, qword ptr ds:[0xFFFFF800040CB020]的下一条指令
这样相当于实现了原来指令相同的功能,访问了原来内核的数据 且没有改变原来eflag的值
访问全局变量其他形式类似处理
mov r,[disp32] -> mov r,[r]
mov [disp32],r-> mov [r],r
mov [disp32],imm32 ->mov [r],imm32
把这个思路搞清楚 64位内核重载就不远了