继续PE系列条记的更新
PE别的条记索引可前去:
PE文件条记一 PE先容
颠末前面的实战PE文件条记八 实战之HOOK步伐添加弹窗和PE文件条记九 实战之HOOK步伐添加弹窗续,对PE文件结构有了进一步的了解之后继续来学习对节的操作
此篇条记为学习节操作之扩大节
扩大节
为什么要扩大节
在先前的实战中,通过在节表和节之间的空白区写入代码并执行来到达了弹窗的效果,但是如果当想要执行的代码量较大时,即空白区的空间不够时,就可以通过扩大节来解决空白区不敷的问题
扩大节涉及的结构体成员
涉及的节表成员含义VirtualAddress节在内存中的偏移 (RVA)Misc节的实际巨细SizeOfRawData节在文件中对齐后的尺寸PointerToRawData节区在文件中的偏移涉及的扩展PE头成员含义SizeOfImageImage(PE文件)巨细扩大哪个节
既然要扩大节,那么选择哪个节举行扩大比力好?
实际上每个节都可以扩大,但是一样平常来说是选取末了一个节举行扩大,为什么?
因为节是顺序存储的,如果扩大了前面的节就意味着背面的节全部都要相应地修改偏移,比力贫苦
选取末了一个节举行扩大就不会影响到背面的节了
扩大节流程
- 将末了一个节的SizeOfRawData和VirtualSize改成N,N = 节内存对齐后的巨细 + 要扩大的巨细
- 修改 SizeOfImage巨细=SizeOfImage巨细+要扩大的巨细
- 分配一块新的空间,巨细为:节内存对齐增加的巨细+要扩大的巨细
按流程扩大节
此次依旧以先前的EverEdit.exe为例举行扩大节的演示
修正节表成员
由于选择的是末了一个节举行扩大,于是不涉及VirtualAddress和PointerToRawData的修改,只需修改Misc和SizeOfRawData即可
先用PE工具DIE查看一下末了一个节的成员
得到了
涉及的节表成员值含义Misc0x1643a节的实际巨细SizeOfRawData0x16600节在文件中对齐后的尺寸于是可以计算出在节内存对齐后的巨细:
节内存对齐后巨细 = (max{Misc,SizeOfRawData}÷SectionAlignment)向上取整 × SectionAlignment
即节内存对齐后巨细 (= max{0x1643a,0x16600}÷0x1000)向上取整 × 0x1000 =(0x16600÷0x1000)向上取整 × 0x1000 = 0x17000
计算 节内存对齐后巨细的原理在PE文件条记六 节表和节中已经说明,这里不再赘述
得到的节内存对齐后的巨细后再加上要扩大的巨细:0x1000,即 0x17000+0x1000=0x18000
于是要将Misc和SizeOfRawData都修改为0x00018000
此时节内存对齐增加的巨细 = 节内存对齐后巨细 - SizeOfRawData(节文件对齐后的巨细)
即节内存对齐增加的巨细 = 0x17000 - 0x16600 = 0x17000 - 0x16600 =0xA00
用WinHex找到对应的位置
修改数据,注意小端存储
修正SizeOfImage
用PE工具:DIE查看一下扩展PE头中SizeOfImage
得到原本SizeOfImage的巨细为0x00298000
要修改为 原本巨细+扩大的巨细= 0x00298000 + 0x1000 = 0x00299000
在WinHex中找到SizeOfImage对应的位置
修改为:
分配新空间
利用WinHex打开EverEdit,直接拉到文件的末尾,并选中末了一个字节
然后 编辑→粘贴0字节
按"是"
选择插入的巨细为:节内存对齐增加的巨细 + 要扩大的巨细 = 0xA00 + 0x1000=0x1A00 对应十进制为6656
添加完成
保存测试
将文件保存后再打开,发现仍旧可以或许正常运行
代码实现扩大节
//扩大末了一个节//第一个参数为指向dos头的指针//第二个参数为指向nt头的指针//第三个参数为存储指向节指针的数组//第四个参数为文件句柄//第五个参数为要扩大的节的巨细void expandSection(_IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr, HANDLE hFile, UINT expandSize) { //获得末了一个节的实际巨细 DWORD VirtualSize = sectionArr[nt->FileHeader.NumberOfSections - 1]->Misc.VirtualSize; //获得末了一个节的文件对齐后的巨细 DWORD SizeOfRawData = sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData; //算出末了一个节内存对齐后的巨细 UINT SizeInMemory = (UINT)ceil((double)max(VirtualSize, SizeOfRawData) / double(nt->OptionalHeader.SectionAlignment)) * nt->OptionalHeader.SectionAlignment; printf("Last Section SizeInMemory:%X\n", SizeInMemory); //根据内存对齐后巨细 - 文件对齐后巨细 得到 节内存对齐增加的巨细 UINT offset = SizeInMemory - sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData; printf("offset:%X\n", offset); //根据节在文件中的偏移 + 文件对齐后的巨细 得到节的末尾 UINT end = sectionArr[nt->FileHeader.NumberOfSections - 1]->PointerToRawData + sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData; printf("end:%X\n", end); //根据要扩大的节的巨细 + 节内存对齐增加的巨细 得到 要分配的新空间巨细 UINT size = offset + expandSize; //设置要写入的地点为节末尾 SetFilePointer(hFile, end, NULL, FILE_BEGIN); //申请要填充的空间 INT* content = (INT*)malloc(expandSize + offset); //初始化为0 ZeroMemory(content, expandSize + offset); //WriteFile用于吸收实际写入的巨细的参数 DWORD dwWritenSize = 0; BOOL bRet = WriteFile(hFile, content, expandSize + offset, &dwWritenSize, NULL); if (bRet) { printf("expand Section success!\n"); //修正节表成员 sectionArr[nt->FileHeader.NumberOfSections - 1]->Misc.VirtualSize = SizeInMemory + expandSize; sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData = SizeInMemory + expandSize; //修正SizeOfImage nt->OptionalHeader.SizeOfImage += expandSize; } else { printf("%d\n", GetLastError()); }}int main(int argc, char* argv[]){ //创建DOS对应的结构体指针 _IMAGE_DOS_HEADER* dos; //读取文件,返回文件句柄 HANDLE hFile = CreateFileA("C:\\Users\\lyl610abc\\Desktop\\EverEdit\\EverEdit.exe", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); //根据文件句柄创建映射 HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 0, 0); //映射内容 LPVOID pFile = MapViewOfFile(hMap, FILE_SHARE_WRITE, 0, 0, 0); //类型转换,用结构体的方式来读取 dos = (_IMAGE_DOS_HEADER*)pFile; //输出dos->e_magic,以十六进制输出 printf("dos->e_magic:%X\n", dos->e_magic); //创建指向PE文件头标志的指针 DWORD* peId; //让PE文件头标志指针指向其对应的地点=DOS首地点+偏移 peId = (DWORD*)((UINT)dos + dos->e_lfanew); //输出PE文件头标志,其值应为4550,否则不是PE文件 printf("peId:%X\n", *peId); //创建指向可选PE头的第一个成员magic的指针 WORD* magic; //让magic指针指向其对应的地点=PE文件头标志地点+PE文件头标志巨细+标准PE头巨细 magic = (WORD*)((UINT)peId + sizeof(DWORD) + sizeof(_IMAGE_FILE_HEADER)); //输出magic,其值为0x10b代表32位步伐,其值为0x20b代表64位步伐 printf("magic:%X\n", *magic); //根据magic判定为32位步伐照旧64位步伐 switch (*magic) { case IMAGE_NT_OPTIONAL_HDR32_MAGIC: { printf("32位步伐\n"); //确定为32位步伐后,就可以利用_IMAGE_NT_HEADERS来吸收数据了 //创建指向PE文件头的指针 _IMAGE_NT_HEADERS* nt; //让PE文件头指针指向其对应的地点 nt = (_IMAGE_NT_HEADERS*)peId; printf("Machine:%X\n", nt->FileHeader.Machine); printf("Magic:%X\n", nt->OptionalHeader.Magic); //创建一个指针数组,该指针数组用来存储所有的节表指针 //这里相当于_IMAGE_SECTION_HEADER* sectionArr[nt->FileHeader.NumberOfSections],声明了一个动态数组 _IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**) malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections); //创建指向块表的指针 _IMAGE_SECTION_HEADER* sectionHeader; //让块表的指针指向其对应的地点 sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS)); //计数,用来计算块表地点 int cnt = 0; //比力 计数 和 块表的个数,即遍历所有块表 while(cnt< nt->FileHeader.NumberOfSections){ //创建指向块表的指针 _IMAGE_SECTION_HEADER* section; //让块表的指针指向其对应的地点=第一个块表地点+计数*块表的巨细 section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER)*cnt); //将得到的块表指针存入数组 sectionArr[cnt++] = section; //输出块表名称 printf("%s\n", section->Name); } expandSection(dos, nt, sectionArr, hFile, 0x1000); break; } case IMAGE_NT_OPTIONAL_HDR64_MAGIC: { printf("64位步伐\n"); //确定为64位步伐后,就可以利用_IMAGE_NT_HEADERS64来吸收数据了 //创建指向PE文件头的指针 _IMAGE_NT_HEADERS64* nt; nt = (_IMAGE_NT_HEADERS64*)peId; printf("Machine:%X\n", nt->FileHeader.Machine); printf("Magic:%X\n", nt->OptionalHeader.Magic); //创建一个指针数组,该指针数组用来存储所有的节表指针 //这里相当于_IMAGE_SECTION_HEADER* sectionArr[nt->FileHeader.NumberOfSections],声明了一个动态数组 _IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**)malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections); //创建指向块表的指针 _IMAGE_SECTION_HEADER* sectionHeader; //让块表的指针指向其对应的地点,区别在于这里加上的偏移为_IMAGE_NT_HEADERS64 sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS64)); //计数,用来计算块表地点 int cnt = 0; //比力 计数 和 块表的个数,即遍历所有块表 while (cnt < nt->FileHeader.NumberOfSections) { //创建指向块表的指针 _IMAGE_SECTION_HEADER* section; //让块表的指针指向其对应的地点=第一个块表地点+计数*块表的巨细 section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER) * cnt); //将得到的块表指针存入数组 sectionArr[cnt++] = section; //输出块表名称 printf("%s\n", section->Name); } break; } default: { printf("error!\n"); break; } } return 0;}运行效果
可以看到代码执行以后,到达了和前面手动操作一样的效果,并且步伐仍旧可以或许正常运行( •̀ ω •́ )✧
代码说明
代码中修改了先前打开文件的权限,修改其拥有写权限
//读取文件,返回文件句柄 HANDLE hFile = CreateFileA("C:\\Users\\lyl610abc\\Desktop\\EverEdit\\EverEdit.exe", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); //根据文件句柄创建映射 HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 0, 0); //映射内容 LPVOID pFile = MapViewOfFile(hMap, FILE_SHARE_WRITE, 0, 0, 0);在扩大节的相干代码中用到了WriteFile和SetFilePointer;别的的就是单纯的PE内容了,代码中都有注释,这里也就不再赘述了
总结
- 扩大节一样平常选取末了一个节举行扩大
- 扩大末了一个节需要修正的节表成员:Misc和SizeOfRawData 以及修正扩展PE头中的SizeOfImage
- 扩大节后要分配的空间除了要扩大的巨细外还要加上节内存对齐增加的巨细(内存对齐后的巨细-文件对齐后的巨细)
附件
附上本条记中分析的EverEdit文件:点我下载
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |