PE文件笔记十六 代码重定位
本帖最后由 lyl610abc 于 2021-4-9 15:22 编辑继续PE系列笔记的更新
PE其它笔记索引可前往:
(https://www.52pojie.cn/thread-1391994-1-1.html)
------
前面在(https://www.52pojie.cn/thread-1413220-1-1.html)中学习了导入表,接着要学习重定位表,但在学习重定位表前,要先了解一下代码重定位
# 代码重定位
在学习重定位表之前,要知道什么是代码重定位
## 代码重定位定义
代码重定位是把可执行代码从内存的一个地方**移动**到另外一个地方去,**保证该部分代码还能正常执行**
## 重定位的提出
可执行代码从内存的一个地方**移动**到另外一个地方,所有的**字节码(硬编码)均保持不变**;如果代码指令中的某些操作数不**跟着地址发生改变**,势必会导致程序运行出错。这里的某些操作数是指那些使用了**绝对地址**定位的程序指令中的操作数
为更好地学习重定位,结合下面的实例分析:
### 代码
```c
#include <stdio.h>
int main(){
int result=0;
_asm{
_code:
xor eax,eax
mov al,byte ptr ds:
jmp _end
_data:
_emit 0x61
_end:
mov result,eax
}
printf("%X\n",result);
return 0;
}
```
------
### 代码说明
上面的代码十分简短简单,主要看汇编的代码
汇编代码分为三段:代码段\_code、数据段\_data、结束段\_end
------
#### 代码段
代码段就是取出数据段的数据,然后保存到寄存器eax
------
#### 数据段
数据段只存储了一个数据0x61
------
#### 结束段
将先前保存到寄存器eax中的数据赋值给变量result
------
### 运行结果
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210409135527504.png)
可以看到,代码能够正常地取出数据区的内容并打印出来
------
### 反汇编对应的硬编码
```asm
5: _asm{
6: _code:
7: xor eax,eax
0040D73F 33 C0 xor eax,eax
8: mov al,byte ptr ds:
0040D741 3E A0 49 D7 40 00 mov al,ds:
9: jmp _end
0040D747 EB 01 jmp _end (0040d74a)
10: _data:
11: _emit 0x61
0040D749 61 db 61h
12: _end:
13: mov result,eax
0040D74A 89 45 FC mov dword ptr ,eax
14: }
```
------
这里关注一下出现地址的两个地方的硬编码:
#### 第一处
```asm
8: mov al,byte ptr ds:
0040D741 3E A0 49 D7 40 00 mov al,ds:
```
取出0040d749地址对应的数据,取出的数据宽度为一个字节,然后赋值给al(eax的低8位)
可以看到这里的硬编码直接就是写死的地址0040d749(49 D7 40 00 小端存储),为**绝对地址**
------
#### 第二处
```asm
9: jmp _end
0040D747 EB 01 jmp _end (0040d74a)
```
这里的jmp跳转则是采用偏移的方式,其偏移为01(前面的EB 表示jmp)
要跳转的地址 = 当前地址 + 当前指令长度 + 偏移 = 0040D747 + 2 + 1 = 0040D74A
第二处的代码则是采用了**相对地址**的方法进行寻址
------
## 没重定位带来的问题
前面的代码中存在绝对地址,因此如果将代码移到别的地方必然导致结果错误
于是为了验证这一点,进行以下的演示:
首先复制其对应的硬编码:
```
33 C0 3E A0 49 D7 40 00 EB 01 61 89 45 FC
```
------
然后用OD随便打开一个程序
随便选中一行代码,然后 右键→二进制→编辑(或使用快捷键Ctrl+E)
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210409141350198.png)
------
在弹出来的编辑窗口中,取消勾选保持大小,然后将硬编码粘贴进去
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210409141728724.png)
------
得到结果:
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210409141956125.png)
------
可以看到,此时采用相对地址的jmp指令后面跳转的地址发生了变化,依然能够跳转到我们想要的位置
但是mov指令由于采取了绝对地址,它去取的地址仍然是先前的地址,没有对应去取下面的数据:0x61
也因此导致了,代码位置变化以后,其运行的结果就不正确了
------
## 修正重定位
通过前面,可以知道,代码出错的原因在于mov指令后面采用了绝对地址
于是将mov 指令后面的地址改为相对地址即可
### 代码
```c
#include <stdio.h>
int main(){
int result=0;
_asm{
_code:
call _call
xor ebx,ebx
mov bl,byte ptr ds:
jmp _end
_data:
_emit 0x61
_call:
mov eax,dword ptr ss:
ret
_end:
mov result,ebx
}
printf("%X\n",result);
return 0;
}
```
------
### 代码说明
代码中多了一个_call段,该段用来调用call,根据堆栈的特性,获得的里存储的地址为xor ebx,ebx这一行指令的地址
不了解为什么在call里面可以通过这种方法获取到返回地址的可以回顾:[逆向基础笔记七 堆栈图(重点)](https://www.52pojie.cn/thread-1379952-1-1.html)
获取到地址后将地址保存到eax中
之后返回以后通过xor ebx,ebx这一行的地址+偏移8得到_data的地址,此时采用的便是相对地址的方式
### 修正后对应的硬编码
```c
5: _asm{
6: _code:
7: call _call
0040D73F E8 09 00 00 00 call _call (0040d74d)
8: xor ebx,ebx
0040D744 33 DB xor ebx,ebx
9: mov bl,byte ptr ds:
0040D746 3E 8A 58 08 mov bl,byte ptr ds:
10: jmp _end
0040D74A EB 06 jmp _end (0040d752)
11: _data:
12: _emit 0x61
0040D74C 61 popad
13: _call:
14: mov eax,dword ptr ss:
0040D74D 36 8B 04 24 mov eax,dword ptr ss:
15: ret
0040D751 C3 ret
16: _end:
17: mov result,ebx
0040D752 89 5D FC mov dword ptr ,ebx
18: }
```
------
这里关注一下出现地址的地方的硬编码
```asm
7: call _call
0040D73F E8 09 00 00 00 call _call (0040d74d)
```
这里的call调用采用偏移的方式,其偏移为09(前面的E8 表示call)
要跳转的地址 = 当前地址 + 当前指令长度 + 偏移 = 0040D73F + 5 + 9 = 0040D74D
于是此时的代码就全部是采用相对地址了,此时代码就支持重定位了
## 测试重定位后的硬编码
依旧要先将重定位的硬编码复制出来:
```
E8 09 00 00 00 33 DB 3E 8A 58 08 EB 06 61 36 8B 04 24 C3 89 5D FC
```
------
然后用OD随便打开一个程序
随便选中一行代码,然后 右键→二进制→编辑(或使用快捷键Ctrl+E)
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210409141350198.png)
------
在弹出来的编辑窗口中,取消勾选保持大小,然后将硬编码粘贴进去
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210409145959047.png)
------
得到结果:
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210409150032097.png)
可以看到采用了相对地址后,移动完代码的位置其代码依然满足我们的需求
------
F8单步步过到最后一行代码,查看ebx此时的值
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210409150346087.png)
可以看到此时的ebx能够正确地获取到数据区的内容( •̀ ω •́ )✧
------
## 采用重定位技术的好处
从上面的例子中,不难的得出采用重定位技术的好处:
如果在代码编写中使用重定位技术,你就可以**将代码随意地部署到内存中,而不影响程序的运行**
------
# 总结
- 采用重定位技术,可以让代码段在**任意**内存中运行
- 重定位技术的核心就在:**不使用绝对地址,而使用相对地址**
- 使用的是绝对地址还是相对地址,归根结底取决于其**对应的硬编码** 大牛66666赞一个 哇这个太厉害了 需要具备各种知识啊 这个技术太牛了 ,支持楼主 支持楼主,赞一个 前来学习 绝对的大牛啊!学习当中! 萌萌懂懂,不过感谢楼主分享,学习中 感谢分享,学到很多{:301_998:} 绝对的大牛啊!学习当中!
页:
[1]
2