首页
社区
课程
招聘
[原创]浅谈XP下最小PE
发表于: 2018-2-7 05:01 12981

[原创]浅谈XP下最小PE

2018-2-7 05:01
12981

近日,科锐一季度一次的最小PE比赛来临,规则就是手写能弹出对话框的最小PE文件,当然为了照顾我们初学PE对字段还不是很熟悉,允许我们参考Mspaint.exe的PE结构.
课堂上Boss钱给我们讲解了PE最小PE思路,并带领着我们写了一个留有优化余地的185字节的版本PE文件.此文是将课堂笔记整理而成,虽力求完美,但难免因本人的学识浅薄而存在不足之处,望大神们批评指正.

因为节表中PointerToRawData的值是200h,SizeOfRawData的值也是200h.所以要在FOR:100h-FOR:3F0h的位置填充上00,保持对齐.

填充后文件大小为1024KB.

文件偏移从0h开始.将PointerToRawData成员(FOR:B0h)修改为(00 00 00 00h).

保存修改,用OD打开185.EXE文件.转到内存0x00400000,查看导入表是否填充正确.

MessageBoxA的地址已经填入.说明我们导入表填充正确.

对话框标题MyPE,就在(FOR:02h)填充"My",与后面的"PE"刚好组成字符串"MyPE".

对话框内容就选(FOR:0Ch)其实的字符串"MessageBoxA"吧.

PE有一个特性,PE文件加载到内存后,不足一个分页的位置会用0填充,也就是说,PE在文件末尾,有初值为0的默认数据,也就是说如果文件末尾的字节为0,就可以把这个字节删掉,进而减小文件体积.
而且操作系统不对执行属性负责(前提为DEP数据执行保护.如果开了,那么必须要有执行属性才能运行,默认DEP关闭),即使节中没有执行属性,也可运行.如果把读属性去掉,会报C00000005.如果手写PE出现C05错误,那么说明PE格式没有问题,请检查代码.操作系统装载,尝试运行没有错误,因为没有读的权限,所以会报错.如果格式错误,则提示不是有效XX位程序.
而文件头默认就有读属性,所以本文件将节表中文件属性去掉并不影响运行.

从本文件中从(FOR:BCh)-(FOR:C3h)删掉.保存文件后检验成果吧.

185字节,完美运行.

节表中节表名字段还剩8字节,可以将导出表(FOR:B4h)挪到节表名(FOR:9Ch)的位置覆盖.

将目录项中导出表(FOR:84h)目录更新为新地址(00 00 00 90h).

删除尾部,保存运行.

1. DOS头
struct _IMAGE_DOS_HEADER {
WORD e_magic;           //5A 4D                    //(!重要) |"MZ标记"
WORD e_cblp;           //AA CC                    //CC是为了让程序断下
WORD e_cp;               //AA AA                    
WORD e_crlc;           //AA AA                    
WORD e_cparhdr;        //AA AA                    
WORD e_minalloc;       //AA AA                    
WORD e_maxalloc;       //AA AA                    
WORD e_ss;             //AA AA                    
WORD e_sp;             //AA AA                    
WORD e_csum;           //AA AA                    
WORD e_ip;             //AA AA                    
WORD e_cs;             //AA AA                    
WORD e_lfarlc;         //AA AA                    
WORD e_ovno;           //AA AA                    
WORD e_res[4];         //AA AA AA AA AA AA AA AA  
WORD e_oemid;          //AA AA                    
WORD e_oeminfo;        //AA AA                    
WORD e_res2[10];       //20个A                    
DWORD e_lfanew;        //00 00 00 40              //(!重要) |用于定位PE标识
};                                                              

2. NT头                                                         
struct _IMAGE_NT_HEADERS{                                       
DWORD Signature;       //00 00 45 50                            //(!重要) |PE标识
_IMAGE_FILE_HEADER FileHeader;                                  
_IMAGE_OPTIONAL_HEADER OptionalHeader;
}

2. 标准PE头(大小固定)
struct _IMAGE_FILE_HEADER {
WORD  Machine;                               //01 4C           //(!重要) 
WORD  NumberOfSections;                      //00 01           //(!重要) |节总数(1)
DWORD TimeDateStamp;                         //AA AA AA AA         
DWORD PointerToSymbolTable;                  //AA AA AA AA 
DWORD NumberOfSymbols;                       //AA AA AA AA
WORD SizeOfOptionalHeader;                   //00 80           //(!重要) |可选PE头的大小
WORD Characteristics;                        //01 0F           //(!重要) |可执行文件值为10F 
};

3. 可选PE头((大小不固定,32和64不同))
struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;                                  //01 0B           //(!重要) |10B-32位下的PE文件
BYTE MajorLinkerVersion;                     //AA              
BYTE MinorLinkerVersion;                     //AA              
DWORD SizeOfCode;                            //AA AA AA AA     
DWORD SizeOfInitializedData;                 //AA AA AA AA     
DWORD SizeOfUninitializedData;               //AA AA AA AA    
DWORD AddressOfEntryPoint;                   //00 00 00 02     //(!重要) |程序入口点(02,断在CC处)
DWORD BaseOfCode;                            //AA AA AA AA     
DWORD BaseOfData;                            //AA AA AA AA     
DWORD ImageBase;                             //00 40 00 00     //(!重要) |内存镜像基址
DWORD SectionAlignment;                      //00 00 10 00     //(!重要) |内存对齐
DWORD FileAlignment;                         //00 00 02 00     //(!重要) |文件对齐
WORD MajorOperatingSystemVersion;            //AA AA           
WORD MinorOperatingSystemVersion;            //AA AA           
WORD MajorImageVersion;                      //AA AA           
WORD MinorImageVersion;                      //AA AA           
WORD MajorSubsystemVersion;                  //00 04           //(!重要) |子系统版本号            
WORD MinorSubsystemVersion;                  //AA AA           
DWORD Win32VersionValue;                     //AA AA AA AA   
DWORD SizeOfImage;                           //00 00 20 00     //(!重要) |PE文件映射到内存后的尺寸,SectionAlignment的倍数
DWORD SizeOfHeaders;                         //00 00 02 00     //(!重要) |所有头+节表按照文件对齐后的大小
DWORD CheckSum;                              //AA AA AA AA     
WORD Subsystem;                              //00 02           //(!重要) |子系统
WORD DllCharacteristics;                     //00 00           //(!重要) |
DWORD SizeOfStackReserve;                    //00 40 00 00     //(!重要) |初始化时保留的栈大小(桟最大值)
DWORD SizeOfStackCommit;                     //00 00 10 00     //(!重要) |初始化时实际提交的栈大小(实际使用桟大小)
DWORD SizeOfHeapReserve;                     //00 10 00 00     //(!重要) |初始化时保留的堆大小(堆最大值)
DWORD SizeOfHeapCommit;                      //00 01 00 00     //(!重要) |初始化时实际提交的堆大小(实际使用堆大小)
DWORD LoaderFlags;                           //AA AA AA AA     
DWORD NumberOfRvaAndSizes;                   //00 00 00 04     //(!重要) |目录项数目(4),其实最优是2项,有导入表即可
_IMAGE_DATA_DIRECTORY DataDirectory[16];                       //目录项(4个目录项,先全初始化为0)
};

4. 节表
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER
 {
    BYTE Name[IMAGE_SIZEOF_SHORT_NAME];    //EE EE EE EE EE EE EE EE               
    union                                              
        {
            DWORD PhysicalAddress;                      
            DWORD VirtualSize;
        } Misc;                            //00 00 10 00  //(!重要) |提交到内存中大小
    DWORD VirtualAddress;                  //00 00 10 00  //(!重要) |提交到内存中的偏移
    DWORD SizeOfRawData;                   //00 00 20 00  //(!重要) |节在文件中对齐后的尺寸
    DWORD PointerToRawData;                //00 00 02 00  //(!重要) |节在文件中的偏移                          
    DWORD PointerToRelocations;            //AA AA AA AA         
    DWORD PointerToLinenumbers;            //AA AA AA AA          
    WORD NumberOfRelocations;              //AA AA       
    WORD NumberOfLinenumbers;              //AA AA
    DWORD Characteristics;                 //60 AA AA AA  //(!重要) |节的属性 高位给运行属性即可
};
6A 00              push 0
68 02 00 40 00      push 0x00400002
68 0C 00 40 00      push 0x0040000c
6A 00              push 0
FF 15 44 00 40 00 call [0x00400044]
C3                  ret
从0x1E开始填充
6A 00              push 0
68 02 00 40 00      push 0x00400002
EB 3E             jmp  0x00400065



从0x65开始填充
68 0C 00 40 00      push 0x0040000c
6A 00               push 0
FF 15 44 00 40 00 call [0x00400044]
C3                  ret

近日,科锐一季度一次的最小PE比赛来临,规则就是手写能弹出对话框的最小PE文件,当然为了照顾我们初学PE对字段还不是很熟悉,允许我们参考Mspaint.exe的PE结构.
课堂上Boss钱给我们讲解了PE最小PE思路,并带领着我们写了一个留有优化余地的185字节的版本PE文件.此文是将课堂笔记整理而成,虽力求完美,但难免因本人的学识浅薄而存在不足之处,望大神们批评指正.

PE有一个特性,PE文件加载到内存后,不足一个分页的位置会用0填充,也就是说,PE在文件末尾,有初值为0的默认数据,也就是说如果文件末尾的字节为0,就可以把这个字节删掉,进而减小文件体积.
而且操作系统不对执行属性负责(前提为DEP数据执行保护.如果开了,那么必须要有执行属性才能运行,默认DEP关闭),即使节中没有执行属性,也可运行.如果把读属性去掉,会报C00000005.如果手写PE出现C05错误,那么说明PE格式没有问题,请检查代码.操作系统装载,尝试运行没有错误,因为没有读的权限,所以会报错.如果格式错误,则提示不是有效XX位程序.
而文件头默认就有读属性,所以本文件将节表中文件属性去掉并不影响运行.

  • 优化PE大小的思路是基于结构重叠,结构重叠是指将两个结构合并存一个位置上.举个例子,假如一个正常PE文件的PE标识从(FOR:40h)开始,这时候将NT头挪到(FOR:04h)的位置,再将DOS头中e_lfanew成员改为0x0000004,此时DOS头与PE头重叠.
  • 之所以选择XP,是因为Win7对可执行文件的检查很严格,基本没什么文章可做.不支持重叠,而XP支持重叠.
  • 将Mspaint.exe中的DOS头拷贝到新建的1024.exe中.
  • 将Mspaint.exe中的NT头(PE标识+PE头+可选PE头(可选PE头中目录项只拷贝4项))拷贝到新建的1024.exe中.
  • 将Mspaint.exe中的一项节表拷贝到新建的1024.exe中.
  • 根据下列备注修改1024.exe.不重要的字段填充AA,并适当修改对齐.目录项先全部填充0.
  • 附上修改好的图.
  • 因为节表中PointerToRawData的值是200h,SizeOfRawData的值也是200h.所以要在FOR:100h-FOR:3F0h的位置填充上00,保持对齐.

  • 填充后文件大小为1024KB.

  • 将修改好的文件保存为1024.exe,双击运行,如果修改无误,会报0x80000003错误,此时不要灰心,出现这个错误说明PE格式正确.然后以此为模版新建一个文件开始重叠结构.(忘了怎么设置,重现XP经典又亲切的大红叉...)
  • 以上文中的1024.exe文件为模版,在010Edid中新建一名为180.EXE的文件.并将1024.exe中的DOS拷贝到180.EXE中.
  • 将1024.EXE中PE标识(FOR:40h)-数据目录(FOR:D7h)处的NT头数据往185.EXE中DOS头(FOR:04h)的位置往后覆盖.
  • 因为上文中将NT头与DOS头重叠,DOS头中e_lfanew(用于定位PE标识的偏移)成员(FOR:3Ch),被PE头中的SectionAlignment(内存对齐)对齐覆盖成(00 00 10 00h).而我们现在的PE标识如今在(FOR:04h),巧的是WinXp最小支持4对齐.所以我们将内存对齐(FOR:3Ch)与文件对齐(FOR:40h)都更改为(00 00 00 04h).
  • 将1024.EXE中节表拷贝到185.EXE尾部.
  • 因为节名称只是说明性意义,操作系统不参考,所以此处8字节可以随意更改.将(FOR:9Ch)-(FOR:A3h)的位置填充EE(EE并没有特定意义,只是为了区分).
  • 因为我们整个头部所占的大小为C4h,将Misc成员(FOR:A4h)修改为(00 00 00 C4h).
  • 两节合一,所以在内存中从0偏移开始.将VirtualAddress成员(FOR:A8h)修改为(00 00 00 00h)
  • 我们整个头部大小为C4h,满足4对齐,可将SizeOfRawData成员(FOR:ACh)修改为(00 00 00 C4h).
  • 文件偏移从0h开始.将PointerToRawData成员(FOR:B0h)修改为(00 00 00 00h).

  • 映射到内存中要以1000对齐,所以将SizeOfImage成员(FOR:54h)修改为(00 00 10 00h).
  • 所有头与节表的大小为C4h,正好是4对齐,所以将SizeOfHeaders成员(FOR:58h)修改为(C4 00 00 00h).
  • 这四个成员分别为桟保留(FOR:64h)|桟提交(FOR:68h)|堆保留(FOR:6Ch)|堆提交(FOR:70h). 高字节保留为0即可,低3字节可填充为AA.
  • 将刚修改的文件保存,运行.再次弹出不久前见到的0x80000003错误报告.说明刚才修改的格式正确.
  • MessageBoxa我们从(FOR:Ch)填充.
  • 库名称 user32 我们从(FOR:30h)填充.
  • Name成员从(FOR:B4h)填充(00 00 00 30h).
  • (FOR:44h)刚好有8个字节可用,此处可以放IAT表.所以FirstThunk成员(FOR:B8h)我们填充(00 00 00 44h).
  • IAT表以0结尾,所以在(FOR:48h)填充4字节0.
  • MessageBoxA在(FOR:04h)处,前面还有两字节字段,所以在(FOR:44h)填充(00 00 00 44h).
  • 导入表起始位置为(FOR:A8h),因此目录项第二项(FOR:84h)填入(00 00 00 A8).
  • 保存修改,用OD打开185.EXE文件.转到内存0x00400000,查看导入表是否填充正确.

  • MessageBoxA的地址已经填入.说明我们导入表填充正确.

  • 对话框标题MyPE,就在(FOR:02h)填充"My",与后面的"PE"刚好组成字符串"MyPE".

  • 对话框内容就选(FOR:0Ch)其实的字符串"MessageBoxA"吧.

  • 一般情况下,调用185.EXE中MessageBoxA的汇编代码如下:
    6A 00              push 0
    68 02 00 40 00      push 0x00400002
    68 0C 00 40 00      push 0x0040000c
    6A 00              push 0
    FF 15 44 00 40 00 call [0x00400044]
    C3                  ret
    
  • 但此文件中明显没有足够的空间让我们正常填写此代码,只能通过跳转来塞入指令.
  • 将OEP(FOR:2Ch)修改为(00 00 00 1Eh)
  • 从本文件中从(FOR:BCh)-(FOR:C3h)删掉.保存文件后检验成果吧.

  • 185字节,完美运行.

  • 上文是将课堂笔记整理而成,因本人学识浅薄难免有遗漏之处,若有不足之处望各位大哥指出让小弟改正.
  • 最小PE只是为了初学PE的我们熟悉PE结构而已,实战中也并没有谁抠字节写个最小PE文件就为了弹个对话框吧...并且在Win7以上的系统检查越来越严格,不支持重叠.
  • 最小PE比赛中,为了避免在网上参考现成的代码,不能在第四字节放NT头,其他地方随意,因为要做到最优,只能在第四字节放NT头.比赛过后,参考了其他同学的思路,完成了141字节的最小PE.过两天放假了再整理141字节最小PE的笔记发上来吧.
  • 时间不早了,洗洗睡吧...
  1. OS:Windows XP
  2. IDE:010 Editor
  3. 参考:Mspaint.exe
  1. 节表中节表名字段还剩8字节,可以将导出表(FOR:B4h)挪到节表名(FOR:9Ch)的位置覆盖.

  2. 将目录项中导出表(FOR:84h)目录更新为新地址(00 00 00 90h).

  3. 删除尾部,保存运行.

  • 一.题记
  • 二.环境
  • 三.185字节版本PE
  •          3.1 1024字节PE模版
  •                  3.1.1 DOS头
  •                  3.1.2 NT头
  •                  3.1.3 节表
  •                  3.1.4 修改PE文件
  •                  3.1.5 节数据填充00
  •                  3.1.6 0x80000003错误
  •          3.2 185字节PE文件
  •                  3.2.1 DOS头
  •                  3.2.2 初次重叠-NT头

  • [注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

    收藏
    免费 2
    支持
    分享
    最新回复 (14)
    雪    币: 440
    活跃值: (1163)
    能力值: ( LV3,RANK:30 )
    在线值:
    发帖
    回帖
    粉丝
    2
    大佬都睡得那么晚么?
    2018-2-7 09:35
    0
    雪    币: 775
    活跃值: (3420)
    能力值: ( LV7,RANK:140 )
    在线值:
    发帖
    回帖
    粉丝
    3
    厉害了!
    我看过一本书里也讲解过这个,但是看起来太费劲了没看完,知道个大概。
    附送随书的一个PE

    上传的附件:
    2018-2-7 10:31
    0
    雪    币: 99
    活跃值: (2443)
    能力值: ( LV4,RANK:50 )
    在线值:
    发帖
    回帖
    粉丝
    4
    最小PE:  XP下是97字节,win7下是252字节,win10下是268字节。
    2018-2-7 11:30
    0
    雪    币: 775
    活跃值: (2292)
    能力值: ( LV5,RANK:60 )
    在线值:
    发帖
    回帖
    粉丝
    5
    WTL /MD 编译的一个demo 大小41kb,第一次见到时候挺无语的
    上传的附件:
    2018-2-7 13:27
    0
    雪    币: 16007
    活跃值: (3406)
    能力值: ( LV2,RANK:10 )
    在线值:
    发帖
    回帖
    粉丝
    6
    TeLeMan>最小PE:    XP下是97字节,win7下是252字节,win10下是268字节。

    能分享文件下载  ?
    2018-2-7 18:40
    0
    雪    币: 36
    活跃值: (1061)
    能力值: ( LV3,RANK:30 )
    在线值:
    发帖
    回帖
    粉丝
    7
    mark  支持支持,这个能弹helloWorld哦,各种版本的helloWorld
    2018-2-7 20:06
    0
    雪    币: 6818
    活跃值: (153)
    能力值: ( LV2,RANK:10 )
    在线值:
    发帖
    回帖
    粉丝
    8
    2018-2-7 22:31
    0
    雪    币: 99
    活跃值: (2443)
    能力值: ( LV4,RANK:50 )
    在线值:
    发帖
    回帖
    粉丝
    9
    plusv TeLeMan>最小PE: XP下是97字节,win7下是252字节,win10下是268字节。 能分享文件下载 ? [em_3]
    https://github.com/corkami/pocs/tree/master/PE/bin
    tiny*系列
    2018-2-8 09:22
    0
    雪    币: 300
    活跃值: (2477)
    能力值: ( LV2,RANK:10 )
    在线值:
    发帖
    回帖
    粉丝
    10
    mark
    2018-2-8 14:18
    0
    雪    币: 4495
    活跃值: (1561)
    能力值: ( LV2,RANK:10 )
    在线值:
    发帖
    回帖
    粉丝
    11
    我小白,看不懂。。
    2018-2-24 20:34
    0
    雪    币: 26
    活跃值: (79)
    能力值: ( LV3,RANK:30 )
    在线值:
    发帖
    回帖
    粉丝
    12
    支持一下下
    2018-2-26 14:31
    0
    雪    币: 39
    活跃值: (10)
    能力值: ( LV2,RANK:10 )
    在线值:
    发帖
    回帖
    粉丝
    13
    虽然看不懂,但是感觉666
    2018-2-26 15:15
    0
    雪    币: 4900
    活跃值: (2555)
    能力值: ( LV2,RANK:10 )
    在线值:
    发帖
    回帖
    粉丝
    14
    王坤?  笔记做的真详细...
    2018-12-16 02:16
    0
    雪    币: 396
    活跃值: (54)
    能力值: ( LV3,RANK:30 )
    在线值:
    发帖
    回帖
    粉丝
    15
    97个字节,背下来就完事
    2021-3-4 12:58
    0
    游客
    登录 | 注册 方可回帖
    返回
    //