首页
社区
课程
招聘
[原创]手工向可执行程序添加MessageBox弹窗(一)
发表于: 2017-4-21 01:35 28661

[原创]手工向可执行程序添加MessageBox弹窗(一)

2017-4-21 01:35
28661

最近在学习PE结构,网上找到滴水第三期中关于PE的讲解,视频中是用impsg做的讲解,用那个实现起来比较简单,因为impsg文件对齐和内存对齐粒度一样,所以不需要考虑文件对齐和内存对齐,为了加深自己对PE文件的理解,事后我用notepad做实验时候总是出错,主要原因是notepad文件对齐和内存粒度不同导致FOA与RAW转换出错,最后将过程记录下来。文中难免有差错,请指正。

在此之前需要对Windows PE结构有个基本的了解。

1.准备工作

1.1获取MessageBox地址

方法①:

#include <stdio.h>
#include <windows.h>

typedef void (*FuncPointer)(LPTSTR);  // 函数指针  

int main()
{   
    HINSTANCE LibHandle;
    FuncPointer GetAddr;
    // 加载成功后返回库模块的句柄 
    LibHandle = LoadLibrary("user32");  
    printf("user32 LibHandle = 0x%X\n", LibHandle);
    // 返回动态链接库(DLL)中的输出库函数地址
    GetAddr=(FuncPointer)GetProcAddress(LibHandle,"MessageBoxA");   
    printf("MessageBoxA = 0x%X\n", GetAddr);
    return 0;
}

方法②: 用OllyDBG加载notepad,左下角command框中输入bp MessageBoxA下断点。查看断点即可得MessageBox地址为:0x77D507EA

1.2CALL和JMP的计算

#include <stdio.h>
#include <windows.h>
void func()
{
    MessageBox(0, 0, 0, 0);
}
int main()
{
    func();    
    return 0;
}

main函数反汇编:

11:       func();
0040D478 E8 8D 3B FF FF       call        @ILT+5(func) (0040100a)
12:       return 0;
0040D47D 33 C0                xor         eax,eax

得到CALL的硬编码是E8
call func继续跟进去:

0040100A E9 01 00 00 00       jmp         func (00401010)
0040100F CC                   int         3

得到JMP的硬编码是E9
func函数反汇编:

6:        MessageBox(0, 0, 0, 0);
00401028 8B F4                mov         esi,esp
0040102A 6A 00                push        0
0040102C 6A 00                push        0
0040102E 6A 00                push        0
00401030 6A 00                push        0
00401032 FF 15 AC A2 42 00    call        dword ptr [__imp__MessageBoxA@16 (0042a2ac)]

观察到:

  地址           机器指令              汇编指令                 指令所占字节数
0040D478    E8 8D 3B FF FF   call @ILT+5(func)(0040100a)       5
0040D47D

E8后边的值并不是真正我们要调用的函数地址, 他们有以下关系:

E8这条指令的下一行地址    E8当前指令的地址    E8当前指令所占大小
     0x0040D47D     =     0x0040D478      +        0x5

真正要跳转的地址      E8后边的值       E8当前指令的下一行地址
  0x0040100A    =    0xFFFF3B8D     +    0x0040D47D

总结出:

E8后边的值 = 真正要跳转的地址 - (E8当前指令的地址 + E8当前指令所占大小)
           = 真正要跳转的地址 - E8当前指令的下一行地址

1.3构造shellcode

通过之前的学习,可以构造出:

// 18 Bytes
shellcode [] = {0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00,  /* MessageBox4个参数入栈 */  
0xE8, 0x00, 0x00, 0x00, 0x00, /* 调用MessageBox */
0xE9, 0x00, 0x00, 0x00, 0x00  /* 跳到原程序的入口(AddressOfEntryPoint) */
};

2.代码区添加shellcode

2.1分析PE结构

                                                                                 图1

图2具体信息.png

                                                                                图2  

从图2中可以得到以下信息:

IMAGE_OPTIONAL_HEADER(可选头)中部分成员信息:
DWORD   AddressOfEntryPoint;  // 程序执行入口RVA      (0x0000739D)
DWORD   ImageBase; // 程序的优先装载地址(基址)       (0x01000000)
// 程序运行时,PE装载器先创建进程,再将文件载入内存,然后把EIP寄存器的值设置为:ImageBase + AddressOfEntryPoint;
DWORD   SectionAlignment;  // 内存中节的对齐粒度       (0x00001000)
DWORD   FileAlignment;  // 文件中节的对齐粒度          (0x00000200
IMAGE_SECTION_HEADER(节表第一项即代码区部分成员信息):
DWORD   VirtualSize;  // 节区在内存中没有对齐前的实际大小 (0x00007748)
DWORD   VirtualAddress;  // 节区在内存中起始位置(RVA)  (0x00001000)
DWORD   SizeOfRawData;  // 节区在文件中对齐后的大小      (0x00007800) 
DWORD   PointerToRawData;  // 节区在在文件中的偏移       (0x00000400)

2.2判断空闲区域是否能放下shellcode

计算文件中代码区空闲空间

SizeOfRawData(0x7800) - VirtualSize(0x007748) >  0x12

空闲空间大于shellcode长度,可以放得下。

2.3将构造好的ShellCode写入空闲区:

PointerToRawData(0x0400)+SizeOfRawData(0x7800)=0x7C00,

在0x7B48与0x7C00之间写入shellcode:

图3.png

                                                 图3

2.4计算E8后边的值

在计算相关值由文件映射到内存时,需要考虑内存对齐和文件对齐。

真正要跳转的地址:MessageBox地址:0x77D507EA

文件中,E8下一行地址相对PointerToRawData偏移量:0x7B5D - 0x400 = 0x775D  

映射到内存中,E8下一行地址:ImageBase + VirtualAddress + 0x775D = 0x0100875D 

E8后边的值:MessageBox - 0x0100875D = 0x76D4808D

2.5计算E9后边的值

要保证MessageBox关闭后,程序能够正常运行,需要jmp到原来的OEP

原来OEP(真正要跳转的地址):ImageBase + AddressOfEntryPoint = 0x0100739D

文件中,E9下一行地址相对PointerToRawData偏移量:0x7B62 - 0x400 = 7762  

映射到内存中,E9下一行地址:ImageBase + VirtualAddress + 0x7762 = 0x01008762 

E9后边的值:`0x0100739D - 0x01008762 = 0xFFFFEC3B`

2.6修改OEP(AddressOfEntryPoint)

文件中shellcode起始地址相对PointerToRawData偏移量:0x7B50 - 0x400 = 0x7750  

映射到内存中,相对ImageBase偏移:VirtualAddress + 0x7750 = 0x8750  

将原来的OEP修改为,映射到内存后的shellcode起始地址(0x8750)。

另存文件,双击,成功弹出MessageBox,如图4:

图4.png


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 2
支持
分享
打赏 + 1.00雪花
打赏次数 1 雪花 + 1.00
 
赞赏  CCkicker   +1.00 2017/05/08
最新回复 (16)
雪    币: 44229
活跃值: (19955)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
感谢分享,这文章适合新手学习
2017-4-21 15:41
0
雪    币: 2347
活跃值: (58)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
构造PE和插shellcode是一个复杂的过程呢!
2017-4-21 16:13
0
雪    币: 6048
活跃值: (3125)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢分享,这文章适合我等新手学习
2017-4-21 20:11
0
雪    币: 1369
活跃值: (4183)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
5
挺棒的
2017-4-23 19:21
0
雪    币: 8
活跃值: (58)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
这也能加精...
2017-4-23 19:57
0
雪    币: 259
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
楼主有没研究过怎么直接调用messageboxa呢?最近在研究这个,怎么能直接得到messageboxa的地址,然后call一下,目前只能用call  [0042a2ac]去实现,但我用od调出了[0042a2ac]的值,然后直接call这个值,程序在od调试时能成功,但正常运行时出错。。,我在win10下的
2018-3-24 16:57
0
雪    币: 8084
活跃值: (1515)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
8
学习了,谢谢分享
2018-5-2 10:33
0
雪    币: 77
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
学习了,谢谢分享。。。
2018-5-2 18:23
0
雪    币: 36
活跃值: (102)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
谢谢分享
2018-10-10 17:08
0
雪    币: 60
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
程序在内存中真正运行的地址怎么求啊,是FOA转RVA然后加上imagebase么,还有SectionAlignment和FileAlignment不相同的话和这个有关系么?
最后于 2019-4-17 16:40 被mb_nzrqtgjl编辑 ,原因:
2019-4-17 15:20
0
雪    币: 1398
活跃值: (793)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
我的熊猫 楼主有没研究过怎么直接调用messageboxa呢?最近在研究这个,怎么能直接得到messageboxa的地址,然后call一下,目前只能用call [0042a2ac]去实现,但我用od调出了[00 ...
这个messagebox的地址不应该是1000 0000以后的值吗?
2019-10-25 22:17
0
雪    币: 1398
活跃值: (793)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
mb_nzrqtgjl 程序在内存中真正运行的地址怎么求啊,是FOA转RVA然后加上imagebase么,还有SectionAlignment和FileAlignment不相同的话和这个有关系么?
真正的运行地址就是你用ue打开看到的地址减去pointertorawdata➕rva+基址就是就是真正运行的地址了,后边那两个就是文件中和内存中对齐的标准
最后于 2019-10-25 22:28 被zhuAE86编辑 ,原因: 写错了
2019-10-25 22:23
0
雪    币: 0
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
楼主,有个地方不清楚。为什么用e8指令调用api,而不用ff 15指令。反汇编代码里面就是用的ff 15调用的messagebox。
2019-12-24 17:21
0
雪    币: 86
活跃值: (77)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
麻烦大佬看一下,实在不知道问题出在哪里。。
2019-12-30 21:50
0
雪    币: 194
活跃值: (79)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
64位pe程序怎么加messagebox?
2020-3-9 20:29
0
雪    币: 345
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
我在VS中所显示的MessageBox的反汇编压参不是6A 00。而是 

MessageBox(0, 0, 0, 0);
00007FF63C87173A 45 33 C9             xor         r9d,r9d  
00007FF63C87173D 45 33 C0             xor         r8d,r8d  
00007FF63C871740 33 D2                xor         edx,edx  
00007FF63C871742 33 C9                xor         ecx,ecx  
00007FF63C871744 FF 15 06 EA 00 00    call        qword ptr [__imp_MessageBoxA (07FF63C880150h)]  
不知道是什么情况。。
2021-3-15 10:28
0
游客
登录 | 注册 方可回帖
返回
//