这是重定位的典型用法
1.为什么要重定位?
PE文件加载到内存都有个 基地址 就是,从哪个线性地址开始 映射可执行文件 内容,比如
EXE通常都是 00400000h,DLL一般是 10000000h,这就是为什么你经常看到的 为什么,反汇编出来的
代码都在 线性地址00400000h左右的原因。那么系统是不是 总是加载 到 这两个 地址呢?
答案是--不是 这个基地址存在于 PE 文件的文件头的 ImageBase (一个dword量)中,连接生成
程序时 可以 自己指定,若不指定那么 默认就是如果是 EXE文件00400000h,DLL文件就是10000000h
对于EXE来说,由于不同进程地址空间是相互隔离的,因此不会有其它文件来抢它的位置,也就是说,
它总能如意的在自己的 ImageBase 处,开始映射。
而dll就不一样了,为什么?
因为 设想 一个EXE文件的两个DLL的默认 ImageBase 都是10000000h,那么必定有一个不能如愿---
着另外的就要由操作系统 为它另找一个地方了 ----
那这又跟重定位有什么关系呢???
因为 对于PE文件,当它编译连接后,其中的许多 指令、数据就是绝对的地址值,比如
szString db '0'
push offset szString
那么PE中实际的机器码就应该是 68 20 00 40 00 这种形式,看 00 20 40 00 ,Intel小尾方式 看过来就是 00 40 20 00
就是个绝对地址,这个绝对地址是与 ImageBase 的 00400000h息息相关的。
当某个DLL需要重定位,那么它的直接寻址指令(就是翻译成机器码后成为绝对数值 的那些地址),就必须修正
否则会访问到错误的地址。
比如 原 ImageBase == 10000000h,实际加载基址 == 20000000h
那么那些直接寻址指令就得加上这个 差值,差值= 实际加载基址 - 默认加载地址
2.重定位技术
MAIN1:
PUSHAD
CALL NEXT
NEXT:
POP EBP
SUB EBP ,OFFSET NEXT
编译结束时:
offset 是个操作符 OFFSET NEXT 回转化为 以默认加载地址为基址的 一个线性地址值,也就是 NEXT 标号的地址,比如 10002000h
也就是 POP EBP这条指令 的首地址
实际执行时:
call 把当前 EIP 入栈,转到标号 NEXT 执行,巧妙就在于,此时 的 EIP 是 CALL 的下一条 指令的首地址即 POP EBP 当前首地址,
POP EBP 弹出这个EIP到EBP 比如 20002000h,此时 一减 就是 实际地址与默认地址的差值了,以后利用这个差值来修正直接寻址的数值。
(用EBX比较好,EBP还是不要乱动)
这段代码常见于 插入到 其他文件 或 进程 的程序中。
上面说的 DLL 文件 有重定位节 不用这个技术。