在PE中添加,删除SECTION
这两天空闲,自己写了个添加,删除SECTION的程序,发现里面的技巧很多,需要注意的地方也很多,现把心得写上,希望能给正在学习PE文件的朋友提供方便。
关于PE的格式,PE的基本知识,请参看其他相关文章。
添加SECTION
添加SECTION的前提是在最后一个SECTION与第一个区块之间有足够的空间)添加一个新SECTION 40字节。通常情况下,是有40字节的空间的,但是如果没有就需要想办法腾出40字节空间来。
通常有2种方法腾空间
重新编排PE头
通常IMAGE_DOS_HEADER 和 IMAGE_NT_HEADER不是紧挨着,中间有一些其他数据,比如DOS占位程序,这些数据基本上没用,可以将NT头以及SECTION数据前移覆盖这些数据达到扩充PE头空余空间添加新SECTION。
IMAGE_DOS_HEADER 的e_flanew 指向NT头相对于文件头的偏移,将NT头前移,只需修改e_flanew的值即可。
优点:相对于扩充PE头,不需要修改所有SECTION的PointerToRawData指向,不用移动所有区块。
缺点:腾出来的尺寸只能加入有限的新SECTION.
扩充PE头
这种方法通常将SizeOfHeader的尺寸扩大文件对齐字节,0x200字节,然后把所有SECTION的PointerToRawData值得加上0x200字节,然后再将所有区块数据向后移动0x200字节。当然SizeOfHeader 也需要加0x200字节。
优点:使用这种方法可以实现无限的添加新SECTION。
缺点:需要改动的地方比较多。
注意绑定输入表
有了空间不一定就能添加新SECTION,还需要查看绑定输入表,绑定输入表和其他节表不同,绑定输入表紧挨着最后的一个SECTION存放,绑定输入表而且必须在PE头区内,由IMAGE_DATA_DIRECTORY[11]来指向。
我手上的PE编辑工具,LoardPE,PETools,StudPE都忽视了这个问题,添加SECTION后都覆盖了绑定输入表数据,而且也没有IMAGE_DATA_DIRECTORY[11],导致添加节区后PE文件不能运行。
如果不想保留绑定输入,那么添加SECTION的时候直接把IMAGE_DATA_DIRECTORY[11]内的数据置0.
如果想保留绑定输入表,那么就先把绑定数据拷贝出来,添加完SECTION再把数据复制到这个SECTION的后面,然后修改IMAGE_DATA_DIRECTORY[11]的指向就可以。
IMAGE_SECTION_HEADER 结构内容
Name 新区块的名字,注意不能超过8字节大小。
VirtualAddress 虚拟地址;这个地址就是PE文件装载后,这个区块所在的内存地址。地址必须是"内存页对齐",必须紧挨着上一个区块。计算公式就是上一个区 块的VirtualAddress + VirtualSize 内存页对齐后的值。
VirtualSize 区块内数据的实际大小;通过数据的实际大小,就可以计算出文件对齐后的数据大小。
SizeOfRawData 区块数据文件对齐后的数据大小。根据VirtualSize计算可计算出文件对齐后的尺寸。
PointerToRawData 区块相对于文件头的文件偏移, 在文件中两区块数据之间不必紧挨着,但起始地址必须是"文件对齐"的。
Characteristics 区块的属性,可根据添加区块的实际目的设置属性,拿不准就设置E0000060代表可读,可写,可执行,包含代码,包含初始化数据。
添加SECTION后还需要添加这个SCTION的区块数据,首先需要把文件尺寸扩大,扩大的尺寸就是区块数据文件对齐后的大小。
最后将IMAGE_OPTIONAL_HEADER32的SizeOfImage 加上新区块数据的VirtualAddress。
将IMAGE_FILE_HEADER 的NumberOfSection 加 1。
删除SECTION
删除一个SECTION,需要考虑的比较多;
删除SECTION不能根据名称删除,应该根据序号删除,因为允许存在同名的Section。
如果删除的Section位于中间位置,后面Section的PointerToRawData文件偏移可以向上移动,但是VirtualAddress虚拟地址就不能简单的向上移动了,因为如果某个节区的VA发生变化,那么这个节区里的所有RVA也会发生变化,而要修复这些RVA工作量就太大了。处理办法就是,把要删除的这个Section的上一个Section的VirtualSize扩大,也就是加上要删除的这个Section的VirtualSize,这样,后面的所有Section的VirtualAddress不变,只修正PointerToRawData就可以。当然SizeOfImage也就不用调整了,PE映像大小没变,只改变了PE文件大小。
如果删除的是最后一个Section,那么删除后将SizeOfImage减小就可以,不存在VA问题。
如果删除的Section和IMAGE_NT_HEADER的DATA_DIRECTORY有关,比如重定位表,TLS表,那么必须要清除数据目录的VirtualAddress,和VirtualSize。
最后,如果有绑定数据,需要在文件中上移一个IMAGE_SECTION_HEADER尺寸,然后修正数据目录表的指向。
附上一段小代码,一个完整的添加删除SECTION例子:
.686 .model flat,stdcall option casemap:none include windows.inc include kernel32.inc includelib kernel32.lib include user32.inc includelib user32.lib include comdlg32.inc includelib comdlg32.lib include Shell32.inc includelib Shell32.lib IDD_DIALOG equ 100 IDC_RICHEDIT equ 1006 IDC_EDIT_NAME equ 1001 ID_OK equ 1008 ID_DEL equ 1009 IDC_EDIT_SIZE equ 1010 _OpenFile proto .data hInstancd dd ? hWinMain dd ? hRichHandle dd ? lpMem dd ? dwFileSize dd ? hMapFile dd ? hFile dd ? szFileName db MAX_PATH dup(?) .const szMsg db '节名称,取前8个字符',0 szMsg2 db '添加时填入节尺寸,删除时填入节编号',0 szDllName db 'RichEd20.dll',0 szFilter db 'PE Files',0,'*.exe;*.dll;*.scr;*.fon;*.drv',0 szErrOpen db '打开文件失败',0 szErrMap db '创建文件映射失败',0 szErrView db '创建文件视图失败',0 szErrPE db '不是PE文件',01 szFmtSection db '%-4d %-8s %08X %08X %08X %08X %08X',0dh,0ah,0 szMsgSection db '----------------------------------------------------------------',0dh,0ah db '序号 节区名称 节区大小 虚拟地址 Raw_尺寸 Raw_偏移 节区属性',0dh,0ah db '----------------------------------------------------------------',0dh,0ah,0 .code _AppendInfo proc _lpsz local @stCR:CHARRANGE pushad invoke GetWindowTextLength,hRichHandle mov @stCR.cpMin,eax mov @stCR.cpMax,eax invoke SendMessage,hRichHandle,EM_EXSETSEL,0,addr @stCR invoke SendMessage,hRichHandle,EM_REPLACESEL,FALSE,_lpsz popad ret _AppendInfo endp ;计算对齐 _Align proc uses edx dwSize,dwAlign mov eax,dwSize xor edx,edx div dwAlign test edx,edx jz @f inc eax @@: mul dwAlign ret _Align endp ;删除Section ;参数:dwNo Section序号 _DelSection proc dwNo local @dwDataSize,@dwSNum,@dwWriteSize,@dwMemAlign local @hMap,@lpMem mov esi,lpMem add esi,[esi+3CH] assume esi:ptr IMAGE_NT_HEADERS push [esi].OptionalHeader.SectionAlignment pop @dwMemAlign ;Section序号是否正确 movzx ecx,[esi].FileHeader.NumberOfSections dec ecx mov @dwSNum,ecx cmp ecx,dwNo jb Err invoke CreateFileMapping,INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,dwFileSize,NULL cmp eax,NULL je Err mov @hMap,eax invoke MapViewOfFile,@hMap,FILE_MAP_WRITE,0,0,0 cmp eax,NULL je Err ;拷贝文件头 mov @lpMem,eax add si,[esi].FileHeader.SizeOfOptionalHeader add esi,sizeof IMAGE_FILE_HEADER add esi,4 mov ecx,esi sub ecx,lpMem mov esi,lpMem mov edi,@lpMem rep movsb assume esi:ptr IMAGE_SECTION_HEADER assume edi:ptr IMAGE_SECTION_HEADER xor edx,edx mov @dwDataSize,edx mov @dwWriteSize,edx ;循环拷贝Section,和Section节数据 要删除的跳过。 loopsec: cmp edx,dwNo je delsec ;拷贝Section mov ecx,sizeof IMAGE_SECTION_HEADER rep movsb ;修正Section PointerOfRawData mov eax,@dwDataSize sub [edi-40].PointerToRawData,eax ;拷贝节区数据 push esi push edi mov eax,[esi-40].PointerToRawData mov ecx,[esi-40].SizeOfRawData mov esi,lpMem add esi,eax mov edi,@lpMem add edi,eax sub edi,@dwDataSize rep movsb pop edi pop esi jmp nextSection delsec: push [esi].SizeOfRawData pop @dwDataSize ;清除相关的DATA_DIRECTORY数据 push edi mov edi,@lpMem add edi,DWORD ptr [edi+3CH] assume edi:ptr IMAGE_NT_HEADERS add edi,4 add edi,sizeof IMAGE_FILE_HEADER add edi,60H assume edi:ptr IMAGE_DATA_DIRECTORY mov ecx,16 loopDD: mov eax,[esi].VirtualAddress cmp eax,[edi].VirtualAddress jne @f mov [edi].VirtualAddress,0 mov [edi].isize,0 @@: add edi,sizeof IMAGE_DATA_DIRECTORY loop loopDD assume edi:nothing pop edi ;删除的是最后一个Section cmp edx,@dwSNum jne secmid push edi mov edi,@lpMem add edi,DWORD ptr [edi+3CH] assume edi:ptr IMAGE_NT_HEADERS invoke _Align,[esi].Misc.VirtualSize,@dwMemAlign sub [edi].OptionalHeader.SizeOfImage,eax pop edi assume edi:ptr IMAGE_SECTION_HEADER jmp nextSection ;删除的是中间的Section,修正上一个Section虚拟大小,需要计算对齐 secmid: mov eax,[esi].Misc.VirtualSize invoke _Align,eax,@dwMemAlign add [edi-40].Misc.VirtualSize,eax add esi,sizeof IMAGE_SECTION_HEADER nextSection: inc edx cmp edx,@dwSNum jle loopsec ;修正NT_HEADER NumberOfSections,复制绑定输入 mov edi,@lpMem add edi,DWORD ptr[edi+3CH] assume edi:ptr IMAGE_NT_HEADERS dec [edi].FileHeader.NumberOfSections mov ecx,[edi].OptionalHeader.DataDirectory[88].isize test ecx,ecx jz @f mov edx,[edi].OptionalHeader.DataDirectory[88].VirtualAddress mov ebx,edx sub ebx,sizeof IMAGE_SECTION_HEADER mov [edi].OptionalHeader.DataDirectory[88].VirtualAddress,ebx movzx eax,[edi].FileHeader.NumberOfSections add di,[edi].FileHeader.SizeOfOptionalHeader add edi,sizeof IMAGE_FILE_HEADER add edi,4 imul eax,40 add edi,eax mov esi,lpMem add esi,edx rep movsb ;更新文件 @@: invoke UnmapViewOfFile,lpMem invoke CloseHandle,hMapFile invoke WriteFile,hFile,@lpMem,dwFileSize,addr @dwWriteSize,NULL mov ecx,dwFileSize sub ecx,@dwDataSize invoke SetFilePointer,hFile,ecx,NULL,FILE_BEGIN invoke SetEndOfFile,hFile invoke CloseHandle,hFile invoke UnmapViewOfFile,@lpMem invoke CloseHandle,@hMap invoke _OpenFile Err: ret _DelSection endp ;添加Section _AddSection proc scName,dwSize local @dwSNum,@dwHeadSize,@dwDataSize,@dwSizeOfHeader,@dwWriteSize,@dwBindSize,@dwBindAdd local @dwFileAlign,@dwMemAlign local @lpMem,@hMap lea eax,dwSize mov esi,lpMem assume esi:ptr IMAGE_DOS_HEADER add esi,[esi].e_lfanew assume esi:ptr IMAGE_NT_HEADERS push [esi].OptionalHeader.FileAlignment pop @dwFileAlign push [esi].OptionalHeader.SectionAlignment pop @dwMemAlign push [esi].OptionalHeader.SizeOfHeaders pop @dwSizeOfHeader movzx ecx,[esi].FileHeader.NumberOfSections mov @dwSNum,ecx push DWORD ptr[esi+0D4H] pop @dwBindSize push DWORD ptr[esi+0D0H] pop @dwBindAdd ;计算PE头剩余空间,最后一个Section 到第一个节数据直接的空间,包括绑定输入数据的尺寸 add si,[esi].FileHeader.SizeOfOptionalHeader add esi,4 add esi,sizeof IMAGE_FILE_HEADER assume esi:ptr IMAGE_SECTION_HEADER mov eax,[esi].PointerToRawData add eax,lpMem @@: add esi,sizeof IMAGE_SECTION_HEADER loop @b mov ebx,esi add ebx,@dwBindSize sub eax,ebx cmp eax,sizeof IMAGE_SECTION_HEADER mov @dwHeadSize,0 ja @f ;计算PE头需要添加的尺寸 push @dwFileAlign pop @dwHeadSize @@: ;计算文件尾需要添加的尺寸 invoke _Align,dwSize,@dwFileAlign mov @dwDataSize,eax ;创建新内存映射对象,尺寸为原文件大小加扩充大小 mov ecx,dwFileSize add ecx,@dwHeadSize add ecx,@dwDataSize invoke CreateFileMapping,INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,ecx,NULL test eax,eax jz errMap mov @hMap,eax invoke MapViewOfFile,eax,FILE_MAP_WRITE,0,0,0 test eax,eax jz errView mov @lpMem,eax ;拷贝PE头 mov esi,lpMem mov edi,@lpMem mov ecx,@dwSizeOfHeader rep movsb ;拷贝节区数据 add edi,@dwHeadSize mov ecx,dwFileSize sub ecx,@dwSizeOfHeader rep movsb ;修正新映像文件头 mov esi,@lpMem assume esi:ptr IMAGE_DOS_HEADER add esi,[esi].e_lfanew assume esi:ptr IMAGE_NT_HEADERS ;如果有绑定表修正 mov ecx,@dwBindSize test ecx,ecx jz @f mov eax,sizeof IMAGE_SECTION_HEADER add DWORD ptr[esi+0D0H],eax ;节数目修正 @@: inc [esi].FileHeader.NumberOfSections ;PE头大小修正 mov ecx,@dwSizeOfHeader add ecx,@dwHeadSize mov [esi].OptionalHeader.SizeOfHeaders,ecx ;PE映像大小修正 invoke _Align,dwSize,@dwMemAlign add [esi].OptionalHeader.SizeOfImage,eax ;定位第一个Section add si,[esi].FileHeader.SizeOfOptionalHeader add esi,4 add esi,sizeof IMAGE_FILE_HEADER assume esi:ptr IMAGE_SECTION_HEADER ;循环修正所有Section数值 PointerOfRawData; mov ecx,@dwSNum mov edx,@dwHeadSize @@: add [esi].PointerToRawData,edx add esi,40 loop @b ;添加新Section mov ecx,[esi-40].VirtualAddress add ecx,[esi-40].Misc.VirtualSize invoke _Align,ecx,@dwMemAlign mov [esi].VirtualAddress,eax mov eax,dwSize mov [esi].Misc.VirtualSize,eax mov ecx,[esi-40].PointerToRawData add ecx,[esi-40].SizeOfRawData mov [esi].PointerToRawData,ecx push @dwDataSize pop [esi].SizeOfRawData mov [esi].Characteristics,0E0000060H lea edi,[esi].Name1 mov esi,scName mov ecx,8 rep movsb ;复制绑定表数据 mov ecx,@dwBindSize test ecx,ecx jz @f mov edi,@lpMem add edi,@dwBindAdd add edi,40 mov esi,lpMem add esi,@dwBindAdd mov ecx,@dwBindSize rep movsb ;更新文件 @@: invoke UnmapViewOfFile,lpMem invoke CloseHandle,hMapFile mov ecx,dwFileSize add ecx,@dwHeadSize add ecx,@dwDataSize invoke WriteFile,hFile,@lpMem,ecx,addr @dwWriteSize,NULL invoke CloseHandle,hFile invoke UnmapViewOfFile,@lpMem invoke CloseHandle,@hMap invoke _OpenFile ret errMap: invoke MessageBox,NULL,offset szErrMap,offset szErrMap,MB_OK ret errView: invoke MessageBox,NULL,offset szErrView,offset szErrView,MB_OK ret _AddSection endp ;显示Section 信息 _PESectionInfo proc local @szBuf[1024]:byte mov esi,lpMem add esi,DWORD ptr [esi+3CH] assume esi:ptr IMAGE_NT_HEADERS movzx ecx,[esi].FileHeader.NumberOfSections add si,WORD ptr [esi].FileHeader.SizeOfOptionalHeader add esi,4 add esi,sizeof IMAGE_FILE_HEADER assume esi:ptr IMAGE_SECTION_HEADER invoke _AppendInfo,offset szMsgSection cld xor ebx,ebx ;循环显示SECTION @@: push ecx invoke wsprintf,addr @szBuf,offset szFmtSection,ebx,addr [esi].Name1,[esi].Misc.VirtualSize,[esi].VirtualAddress,[esi].SizeOfRawData,[esi].PointerToRawData,[esi].Characteristics invoke _AppendInfo,addr @szBuf add esi,sizeof IMAGE_SECTION_HEADER pop ecx inc ebx loop @b ret _PESectionInfo endp ;获取文件名 _OpenFile proc invoke CreateFile,offset szFileName,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL cmp eax,INVALID_HANDLE_VALUE je ErrOpen mov hFile,eax invoke GetFileSize,eax,0 mov dwFileSize,eax invoke CreateFileMapping,hFile,NULL,PAGE_READONLY,0,0,NULL cmp eax,NULL je ErrMap mov hMapFile,eax invoke MapViewOfFile,eax,FILE_MAP_READ,0,0,0 cmp eax,NULL je ErrView ;判断是否为pe文件 mov lpMem,eax ; cmp word ptr [eax],5A4DH cmp word ptr [eax],"ZM" jne ErrPe add eax,[eax+03CH] ; cmp word ptr [eax],4550H cmp word ptr [eax],"EP" jne ErrPe invoke _PESectionInfo ret ErrOpen: lea eax,offset szErrOpen invoke CloseHandle,hFile jmp @f ErrMap: lea eax,offset szErrMap invoke CloseHandle,hMapFile jmp @f ErrView: lea eax,offset szErrView invoke UnmapViewOfFile,lpMem jmp @f ErrPe: mov eax,offset szErrPE jmp @f @@: invoke SendMessage,hWinMain,WM_SETTEXT,NULL,eax ret _OpenFile endp _DlgProc proc hWnd,wMsg,wParam,lParam local @szNameBuf[8]:BYTE mov eax,wMsg cmp eax,WM_CLOSE je wmclose cmp eax,WM_INITDIALOG je wminit cmp eax,WM_COMMAND je wmcommand cmp eax,WM_DROPFILES je wmdrop xor eax,eax jmp finish wminit: push hWnd pop hWinMain invoke SetDlgItemText,hWnd,IDC_EDIT_NAME,offset szMsg invoke SetDlgItemText,hWnd,IDC_EDIT_SIZE,offset szMsg2 invoke GetDlgItem,hWnd,IDC_RICHEDIT mov hRichHandle, eax jmp process wmcommand: mov eax,wParam cmp ax,ID_OK jne @f lea edi,@szNameBuf xor eax,eax mov ecx,8 rep stosb invoke GetDlgItemText,hWnd,IDC_EDIT_NAME,addr @szNameBuf,8 invoke GetDlgItemInt,hWnd,IDC_EDIT_SIZE,NULL,FALSE test eax,eax jz process invoke _AddSection,addr @szNameBuf,eax jmp process wmdrop: invoke DragQueryFile,wParam,0,offset szFileName,MAX_PATH invoke SendMessage,hWnd,WM_SETTEXT,NULL,offset szFileName invoke _OpenFile jmp process @@: cmp ax,ID_DEL jne @f invoke GetDlgItemInt,hWnd,IDC_EDIT_SIZE,NULL,FALSE cmp lpMem,0 jz process invoke _DelSection,eax jmp process @@: jmp finish wmclose: invoke EndDialog,hWnd,0 process: mov eax,1 finish: ret _DlgProc endp start: invoke LoadLibrary,offset szDllName mov hRichHandle,eax invoke GetModuleHandle,NULL mov hInstancd,eax invoke DialogBoxParam,hInstancd,IDD_DIALOG,NULL,offset _DlgProc,NULL invoke ExitProcess,NULL end start