📄 offset.txt
字号:
不要看反汇编代码,把注意力放到机器码上 非常罗嗦,高手就不要看折磨自己了,不过不知道有没有错,欢迎指出来
其实我也刚明白--
dwVar dd ?
call @F
@@:
pop ebx
sub ebx,offset @B
mov eax,[ebx + offset dwVar]
翻译成机器码就是:
:00401000 00000000 BYTE 4 DUP(0)
:00401004 E800000000 call 00401009 call @F
:00401009 5B pop ebx
:0040100A 81EB09104000 sub ebx, 00401009 sub ebx,offset @B
:00401010 8B8300104000 mov eax, dword ptr [ebx+00401000] mov eax,[ebx + offset dwVar]
重点在理解 相对地址(相对跳转,变) 和 绝对地址(绝对跳转,全局变量,不变),难点(我觉得)在这句迷惑人的反汇编代码 call 00401009
不要看反汇编代码,把注意力放到机器码上
把call @F(call 00401009) 这句的机器码 E800000000 和最后两行的机器码对比着看(81EB09104000、8B8300104000)
看出来了吗?81EB和8B83后面的09104000和00104000也就是00401009和00401000这两个是绝对地址(注意反过来了,低地址对低位),因为全局变量用的就是绝对地址。一旦编译好了,不管这段“数据+代码”移到那里,这两个地址都不会变,都指向00401009和00401000,因为这是绝对地址,因为机器码不会变。虽然这时候全局变量(数据)已经从这移走了,现在在这里的有可能是被插入进程自己的数据。
再看机器码E800000000后面是00000000,而不是00401009,这说明什么? 对了:这不是一个绝对地址而是一个相对地址,不是直接跳到00401009,而是跳到当前指令地址+偏移00000000处的地址,也就是当前指令地址。
跟上面的两条指令一样,也移动这句代码,比如移到00801000,机器码不会变还是E800000000,但这时它的反汇编代码会显示成call 00801009,移动一千次,反汇编代码变一千次。所以这句反汇编代码迷惑人的地方就在于实际上代码会变,但代码的反汇编表现形式——call 00401009——给人一种不变的错觉(比如和这行比 sub ebx, 00401009,这里的00401009就是绝对地址),总认为它不管移到哪都跳到00401009,这里不明白再往下看就等着崩溃吧。不知道你卡哪了,反正我就郁闷在这了,所以说基础知识掌握不好会让你学的很郁闷。
回过头再看上面最后两行的反汇编代码,结果是没变,还是sub ebx, 00401009 和 mov eax, dword ptr [ebx+00401000]。
好了,call @F =jmp @F + push @F(没错吧)
pop ebx 把 @F(就是变的值)放到 ebx
sub ebx,offset @B 用变的值-不变的得到差值(offset @B 得到的也是绝对地址)
mov eax,[ebx + offset dwVar] 差值+不变的=变的也就是正确的,然后把后面所有的全局变量都加上
00801009-00401009=00400000,全局变量都加上400000就ok了
如果整个“数据+代码”移到00602000,于是00602009-00401009=00201000,那就加201000
再看下面老罗写的应该就能明白了吧 ——
自定位技术其实很简单,观察下面这段代码:
dwVar dd ?
call @F
@@:
pop ebx
sub ebx,offset @B
mov eax,[ebx + offset dwVar]
翻译成机器码就是:
:00401000 00000000 BYTE 4 DUP(0)
:00401004 E800000000 call 00401009
:00401009 5B pop ebx
:0040100A 81EB09104000 sub ebx, 00401009
:00401010 8B8300104000 mov eax, dword ptr [ebx+00401000]
这段代码不存在重定位的问题,分析如下。
call指令会将返回地址压入到堆栈中,当整段代码在没有移动的情况下执行的时候时,call @F指令执行后堆栈中的返回地址就是@@标号的地址00401009h,下一句pop指令将返回地址弹出到ebx中,再接下来ebx减去00401009h,现在ebx等于0,所以mov eax,[ebx + offset dwVar]指令就等于mov eax,dwVar指令。
当整段代码被移动到其他地方时(假设被移到00801000处执行),@@标号现在对应的地址是00801009h,变量dwVar的地址对应00801000h,当call指令执行后,压入到堆栈的地址是00801009h,pop到ebx中的就是这个数值,经过sub ebx,00401009指令以后,ebx等于00400000h,现在mov eax,dword ptr [ebx+00401000]指令就相当于mov eax,[00801000],而00801000这个地址刚好等于dwVar现在所处的位置,所以,虽然代码被移动了位置,mov eax,dwVar指令还是访问了正确的地方。
call/pop/sub这3个指令组合的用途就是计算出代码当前的位置和设计时位置的偏移值之差,只要用这个差值去修正包含绝对地址的指令,如访问全局变量的指令,就能够保证修正后的地址是正确的,这就解决了重定位的问题。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -