lyl610abc 发表于 2021-4-3 16:13

PE文件笔记九 实战之HOOK程序添加弹窗续

继续PE系列笔记的更新

PE其它笔记索引可前往:
(https://www.52pojie.cn/thread-1391994-1-1.html)

------

在前面的(https://www.52pojie.cn/thread-1408866-1-1.html)中能够使用OD达到在运行态时添加弹窗的功能,接下来则要对先前的反汇编的硬编码稍作修改然后插入到PE文件中,最后再修改入口点即可;具体流程在上个笔记已经说明了,这里不再赘述

# PE实战之给程序添加弹窗续

## 最终效果图

先看一下最终的效果图

![弹窗](https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/弹窗.gif)

------

## 反汇编和硬编码的对应关系

因为后面要将反汇编代码转换为硬编码,于是这里就要研究一下两者的对应关系

先贴上先前得到的反汇编代码

```c
00401130 >    6A 00         push 0x0
00401132      68 52114000   push MessageB.00401152
00401137      68 48114000   push MessageB.00401148
0040113C      6A 00         push 0x0
0040113E      E8 A7F69477   call user32.MessageBoxA
00401143    - E9 8801C4FF   jmp 000412D0
00401148      6c            db 6c
........
```

------

### push对应硬编码

压入的第一个和第四个参数是一个**立即数**0,于是它对应的硬编码固定为6A 00

```assembly
00401130 >    6A 00         push 0x0
0040113C      6A 00         push 0x0
```

前面的6A是固定的,表示压入一个立即数,

后面的字节码表示压入的立即数,该立即数 的范围是0~0x7F即0~127

当立即数大于0x7F后硬编码和其含义就都改变了

```assembly
6A 7F         push 0x7F
68 80000000   push 0x80
```

于是得到了**push 立即数 对应的硬编码为6A 立即数**

------

压入的第二个和第三个参数是**内存地址**0x00401152中存储的数据和0x00401148中存储的数据

```assembly
00401132      68 52114000   push MessageB.00401152
00401137      68 48114000   push MessageB.00401148
```

观察硬编码和压入的地址的关系

前面的68是固定的,表示push 一个内存地址

后面的则是要push的内存地址的小端存储,倒过来看就是压入的内存地址了

举个例子,如果push 的内存地址为0x12345678

则其对应的硬编码为 68 78 56 34 12

```assembly
68 78563412   push 0x12345678
```

于是得到了**push 内存地址 对应的硬编码为 68 内存地址的小端存储**

------

### call对应硬编码

接下来就是调用MessageBoxA了,其形式为:call MessageBoxA对应的**内存地址**

```assembly
0040113E      E8 A7F69477   call user32.MessageBoxA
上面为OD的反汇编引擎为方便我们观看,而显示的内容,下面才是实际的反汇编代码
0040113E      E8 A7F69477   call 77D507EA
```

分析call 内存地址 和 硬编码的关系

前面的E8是固定的,表示**直接**调用一个内存地址

后面的则是偏移,该偏移为小端存储,此时值为:0x7794F6A7

不难发现这个**偏移 = call的内存地址 - 当前的内存地址 - 当前指令的总长度**

即 0x7794F6A7 = 0x77D507EA - 0x0040113E - 5

再举个例子,如果当前要跳转的内存地址为0x12345678

则其对应的 偏移 = 0x12345678 - 0040113E - 5 = 11F44535

```assembly
0040113E      E8 3545F411   call 12345678
```

于是得到了**call 内存地址 对应的硬编码为 E8 偏移的小端存储,其中偏移 = 要调用的内存地址 - 当前的内存地址 - 当前指令的总长度**

------

### jmp对应硬编码

```assembly
00401143    - E9 8801C4FF   jmp 000412D0
```

前面的E9是固定的,表示跳转到一个内存地址

后面的也是偏移,该偏移为小端存储,此时值为:0xFFC40188

偏移 = jmp的内存地址 - 当前的内存地址 - 当前指令总长度

即 0xFFC40188 = 0x000412D0 - 0x00401143 - 5

于是得到了**jmp 内存地址 对应的硬编码为 E9 偏移的小端存储,其中偏移 = 要跳转的内存地址 - 当前的内存地址 - 当前指令总长度**

## 寻找空白区

知道了硬编码如何构造以后,就要在PE文件中找一块空白区,向里面写入要执行的硬编码

有了先前(https://www.52pojie.cn/thread-1393291-1-1.html)的知识,可以知道由于文件对齐,在块表和块之间是存在空隙(空白区)的,于是可以向这块区域写入硬编码

这次用来演示的软件是前面经常出场的EverEdit.exe,直接找到其对应的空白区:

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210403135440893.png)

------

## 构造硬编码

找到空白区的地址为0x2B0,注意此时的地址为文件中的状态

在内存中其对应的地址为ImageBase+0x2B0 = 0x00400000+0x2B0 = 0x004002B0

PS:如果选取的空白区为块中的空白区,则需要进行FOA到VA的转换,相关内容在(https://www.52pojie.cn/thread-1408576-1-1.html)已说明,这里不再赘述

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210403141604370.png)

------

于是从这里开始构造硬编码

根据前面反汇编一行一行来构造硬编码

```assembly
00401130 >    6A 00         push 0x0
00401132      68 52114000   push MessageB.00401152
00401137      68 48114000   push MessageB.00401148
0040113C      6A 00         push 0x0
0040113E      E8 A7F69477   call user32.MessageBoxA
00401143    - E9 8801C4FF   jmp 000412D0
00401148      6c            db 6c
```

------

### push 参数

1.push 0 直接用6A 00填充,无需修改

2.push 内存地址,该内存地址存储第二个参数"tips",因为内存地址尚未填充,先用00代替,为 68 00 00 00 00

3.push 内存地址,该内存地址存储第三个参数"lyl610abc",同样先用00代替,为 68 00 00 00 00

4.push 0 直接用6A 00填充,无需修改

于是得到的硬编码为:6A 00 68 00 00 00 00 68 00 00 00 00 6A 00,将其写入文件中:

```
6A00680000000068000000006A00
```

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210403142658934.png)

------

### call MessageBoxA

call 内存地址,要call的内存地址为MessageBoxA对应的地址,在本机中为0x77D507EA

此时的内存地址为ImageBase+0x2BE= 0x004002BE

于是可以算出 偏移 = 要跳转的地址 - 内存地址 - 当前指令总长度 = 0x77D507EA - 0x004002BE -5 = 0x77950527

于是得到的硬编码为 E8 27 05 95 77,将其写入文件中:

```
E827059577
```

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210403143107638.png)

------

### jmp 程序入口点

所谓的程序入口点就是 扩展PE头中的AddressOfEntryPoint + ImageBase得到的内存地址

在先前的(https://www.52pojie.cn/thread-1405930-1-1.html#37759493_addressofentrypoint)中其实已经看过了,这里再用PE工具:Detect It Easy查看一下:

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210403143747806.png)

------

可以得到AddressOfEntryPoint 为0x16AF12,加上ImageBase得到0x56AF12

于是要跳转的地址就是0x56AF12,对应汇编为 jmp 0x56AF12

此时的内存地址为ImageBase+0x2C3 = 0x004002C3

偏移 = 要跳转的地址 - 此时的内存地址 - 当前指令的总长度 = 0x56AF12 - 0x004002C3 - 5 = 0x0016AC4A

于是得到对应的硬编码为 E9 4A AC 16 00,将其写入文件中:

```
E94AAC1600
```

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210403144457310.png)

------

填充字符串ASCII码

| 参数       | 含义   | 字符串    | ASCII                | 文件中起始地址 | 内存中起始地址 |
| ---------- | -------- | --------- | -------------------- | -------------- | -------------- |
| 第三个参数 | 窗口内容 | lyl610abc | 6C796C36313061626300 | 2C8            | 004002C8       |
| 第二个参数 | 窗口标题 | tips      | 7469707300         | 2D2            | 004002D2       |

填充完如下:

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210403150527290.png)

------

### 修正参数

填充完字符串就可以修正参数了

第二个参数 为 push0x004002D2,对应"tips",对应硬编码为68 D2 02 40 00

```
68D2024000
```

第三个参数为push0x004002C8,对应"lyl610abc",对应硬编码为68 C8 02 40 00

```
68C8024000
```

修正完的结果为:

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210403150921982.png)

------

### 所有的硬编码

```
6A0068D202400068C80240006A00E827059577E94AAC16006C796C363130616263007469707300
```

------

## 修改程序入口点

构造完硬编码后,只要将程序入口点改为硬编码的首地址:0x2B0即可

### 找到原本的程序入口点

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210403151245859.png)

------

### 修改程序入口点

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210403151407916.png)

------

## 保存程序并测试运行

最后便是将程序保存,然后打开

### 保存

我这里另存为了EverEdit2.exe

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210403151545013.png)

------

### 测试运行

![弹窗](https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/%E5%BC%B9%E7%AA%97.gif)

可以看到能够成功地为程序添加弹窗,完毕(~ ̄▽ ̄)~

# 说明

测试的系统**务必为32位的XP系统**,在高版本的系统有**DEP和ASLR**等**保护机制**,使用以上方法修改后**无法运行**

上面的MessageBoxA的地址是**用OD获取的**,具体方法见上一篇笔记,如果想要复现为程序添加弹窗,则**要修改对应的MessageBoxA的地址**

# 附件

附上本笔记中要修改的EverEdit文件:[点我下载](https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/EverEdit.zip?versionId=CAEQHhiBgID6g6vXxBciIDMxMjY5Y2Q0NGE5NTRkNmNiNTUwOGM0YjdmZTQxMTI3)

lyl610abc 发表于 2021-4-3 20:29

aswcy815174418 发表于 2021-4-3 20:18
测试的系统务必为32位的XP系统?
我修改程序入口后程序直接c0000007b报错,我程序入口改到其他空白区就能 ...

因为我这里的空白区取的是节表和节之间的空白区,可能是不具备对应的可执行权限
一般来说是要取的空白区是具备权限的、在节和节之间的那片空白区

lyl610abc 发表于 2021-4-3 23:28

aswcy815174418 发表于 2021-4-3 20:37
经验+1,坐等扩大节

我特地修改为节和节之间具备权限的空白区后去测试了,在WIN10 x64上没有c0000007b报错了
但由于ASLR(地址随机化)的原因 无法push直接的内存地址了,只能改为全部改为push 0,然后修正一下MessageBoxA的地址即可正常运行了:
运行结果:


lyl610abc 发表于 2021-4-6 18:41

taotianc 发表于 2021-4-6 18:13
只有XP才能运行吗

因为高版本系统有 ASLR(地址随机化)和DEP(数据执行保护) 两种系统保护机制
由于地址的随机化,和插入代码权限不匹配的原因是无法执行的
并且不同系统的MessageBoxA是不同的,这次添加的弹窗的代码是一种伪ShellCode
很多内容要到后面才好说明,这次的例子主要是为了巩固PE知识,也就不再继续深入了

aswcy815174418 发表于 2021-4-3 20:18

测试的系统务必为32位的XP系统?
我修改程序入口后程序直接c0000007b报错,我程序入口改到其他空白区就能打开

aswcy815174418 发表于 2021-4-3 20:37

lyl610abc 发表于 2021-4-3 20:29
因为我这里的空白区取的是节表和节之间的空白区,可能是不具备对应的可执行权限
一般来说是要取的空白区 ...

经验+1:lol:lol,坐等扩大节

lyl610abc 发表于 2021-4-3 20:43

aswcy815174418 发表于 2021-4-3 20:37
经验+1,坐等扩大节

先前我所写的RVA和FOA的转换有些错误,正在改正,也在补充FOA转RVA的内容{:301_977:}

ifthen 发表于 2021-4-3 20:55

类似的文章读过一次。用到的时候找不到。我总是不肯下功夫。

opensail 发表于 2021-4-3 23:23

哎,汇编真是好东西啊

shaonianNo1 发表于 2021-4-4 09:20

好东西收藏~~~~

debug_cat 发表于 2021-4-4 09:28

大佬是用吾爱的那个xp虚拟机来写代码的、?我记得爱盘里面有一个吾爱的xp的虚拟机镜像。里面很多工具和环境。
页: [1] 2 3
查看完整版本: PE文件笔记九 实战之HOOK程序添加弹窗续