PE头的应用---插入代码到EXE或DLL文件中
三、代码实现(DELPHI版本),采用第三种方式实现代码插入。
1. 定义两个类,一个用来实现在内存中建立输入表;一个用来实现对PE头的代码插入。
DelphiCode:
const MAX_SECTION_NUM = 20; const DYN_LOADER_START_MAGIC = $C0DE51A9; const DYN_LOADER_END_MAGIC = $C0DEE2DE; const DYN_LOADER_START_DATA1 = $DA1EDA1E; const IMPORT_TABLE_EXE = 0; const IMPORT_TABLE_DLL = 1; Type TIIDUnion = record case Integer of 0: (Characteristics: DWORD); // 0 for terminating null import descriptor 1: (OriginalFirstThunk: DWORD); // RVA to original unbound IAT (PIMAGE_THUNK_DATA) end; PIMAGE_IMPORT_DESCRIPTOR = ^IMAGE_IMPORT_DESCRIPTOR; {$EXTERNALSYM PIMAGE_IMPORT_DESCRIPTOR} _IMAGE_IMPORT_DESCRIPTOR = record Union: TIIDUnion; TimeDateStamp: DWORD; ForwarderChain: DWORD; Name: DWORD; FirstThunk: DWORD; end; {$EXTERNALSYM _IMAGE_IMPORT_DESCRIPTOR} IMAGE_IMPORT_DESCRIPTOR = _IMAGE_IMPORT_DESCRIPTOR; {$EXTERNALSYM IMAGE_IMPORT_DESCRIPTOR} TImageImportDecriptor = IMAGE_IMPORT_DESCRIPTOR; PImageImportDecriptor = PIMAGE_IMPORT_DESCRIPTOR; PIMAGE_TLS_DIRECTORY32 = ^IMAGE_TLS_DIRECTORY32; {$EXTERNALSYM PIMAGE_TLS_DIRECTORY32} _IMAGE_TLS_DIRECTORY32 = record StartAddressOfRawData: DWORD; EndAddressOfRawData: DWORD; AddressOfIndex: DWORD; AddressOfCallBacks: DWORD; SizeOfZeroFill: DWORD; Characteristics: DWORD; end; {$EXTERNALSYM _IMAGE_TLS_DIRECTORY32} IMAGE_TLS_DIRECTORY32 = _IMAGE_TLS_DIRECTORY32; {$EXTERNALSYM IMAGE_TLS_DIRECTORY32} TImageTlsDirectory32 = IMAGE_TLS_DIRECTORY32; PImageTlsDirectory32 = PIMAGE_TLS_DIRECTORY32; PImageImport = ^TImageImport; TImageImport = packed record szLibrary : array[0..31] of char; ThunksList : TList; end; {在内存中建立一个输入表的内存块,地址放在pMem中,大小是dwSize} TImportTableMaker = class private function GetIATSize : DWORD; procedure Init(iType : integer); protected ImportTable : TList; public dwSize : DWORD; pMem : Pointer; constructor Create(iType : integer); destructor Destroy; override; procedure Build(baseRVA : DWORD); end; {用来传递数据的一个结构} PData = ^TData; TData = record dwReserved1 : DWORD; dwFileType : DWORD; dwImageBase : DWORD; dwOrgEntryPoint : DWORD; dwImportVAddr : DWORD; dwRelocationVAddr : DWORD; dwRelocationSize : DWORD; imgTLSDirectory : IMAGE_TLS_DIRECTORY32; end; TPE = class private dwFileSize : DWORD; pMem : Pointer; SectionNum : integer; pNewSection : Pointer; function ReturnToBytePtr(FuncCode : pointer; findstr : DWORD): pointer; procedure SetSectionsWritePermission; procedure CopyData(dwVirtualAddress : DWORD); protected ITMaker : TImportTableMaker; imgDosHeader : TImageDosHeader; pDosStub : Pointer; dwDosStubSize : DWORD; dwDosStubOffset : DWORD; imgNtHeaders : TImageNtHeaders; imgSectionHeaders : array[0..MAX_SECTION_NUM -1] of TImageSectionHeader; imgSections : array[0..MAX_SECTION_NUM -1] of Pointer; imgTLSDirectory : IMAGE_TLS_DIRECTORY32; function PEAlign(dwTarNum : DWORD; dwAlignTo : DWORD) : DWORD; procedure AlignmentSections; function Offset2RVA(dwOffset : DWORD) : DWORD; function RVA2Offset(dwRVA : DWORD) : DWORD; function ImgRVA2Section(dwRVA : DWORD) : PImageSectionHeader; function ImgOffset2Section(dwOffset: DWORD): PImageSectionHeader; function ImgOffset2SectionNum(dwOffset: DWORD): integer; function AddNewSection(szName: string; dwSize: DWORD): PImageSectionHeader; public constructor Create; destructor Destroy; override; procedure OpenFile(filename : string); procedure SaveFile(filename : string); procedure CyrptFile; end; |
二、类代码
DelphiCode:
implementation uses PEInit; //被用来插入的代码单元 const szWindowsAPIs : array[0..10] of string = ('Kernel32.dll', 'GetModuleHandleA', 'VirtualProtect', 'GetModuleFileNameA', 'CreateFileA', 'GlobalAlloc', '', 'User32.dll', 'MessageBoxA', '', ''); const szIATEXEStrings : array[0..4] of string = ('Kernel32.dll', 'LoadLibraryA', 'GetProcAddress', '', ''); const szIATDLLStrings : array[0..29] of string = ( 'Kernel32.dll', 'LoadLibraryA', 'GetProcAddress', 'GetModuleHandleA', '', 'User32.dll', 'GetKeyboardType', 'WindowFromPoint', '', 'AdvApi32.dll', 'RegQueryValueExA', 'RegSetValueExA', 'StartServiceA', '', 'Oleaut32.dll', 'SysFreeString', 'CreateErrorInfo', 'SafeArrayPtrOfIndex', '', 'Gdi32.dll', 'UnrealizeObject', '', 'Ole32.dll', 'CreateStreamOnHGlobal', 'IsEqualGUID', '', 'ComCtl32.dll', 'ImageList_SetIconSize', '', ''); constructor TPE.Create; begin dwDosStubSize := 0; end; destructor TPE.Destroy; begin end; {实现File或Section对齐} function TPE.PEAlign(dwTarNum: DWORD; dwAlignTo: DWORD): DWORD; begin result := ((dwTarNum + dwAlignTo - 1) div dwAlignTo) * dwAlignTo; end; {把所有的节进行Section对齐} procedure TPE.AlignmentSections; var i : integer; begin for i:= 0 to imgNtHeaders.FileHeader.NumberOfSections - 1 do begin imgSectionHeaders[i].VirtualAddress := PEAlign(imgSectionHeaders[i].VirtualAddress, imgNtHeaders.OptionalHeader.SectionAlignment); imgSectionHeaders[i].Misc.VirtualSize := PEAlign(imgSectionHeaders[i].Misc.VirtualSize, imgNtHeaders.OptionalHeader.SectionAlignment); imgSectionHeaders[i].PointerToRawData := PEAlign(imgSectionHeaders[i].PointerToRawData, imgNtHeaders.OptionalHeader.FileAlignment); imgSectionHeaders[i].SizeOfRawData := PEAlign(imgSectionHeaders[i].SizeOfRawData, imgNtHeaders.OptionalHeader.FileAlignment); end; imgNtHeaders.OptionalHeader.SizeOfImage := imgSectionHeaders[i-1].VirtualAddress + imgSectionHeaders[i-1].Misc.VirtualSize; imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress := 0; imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size := 0; imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress := 0; imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size := 0; imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress := 0; imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size := 0; end; {找到内存中某一个RVA所映射的磁盘文件上的offset} function TPE.RVA2Offset(dwRVA : DWORD): DWORD; var offset : DWORD; section : PImageSectionHeader; begin section := ImgRVA2Section(dwRVA); if section = nil then result := 0 else begin offset := dwRVA + section.PointerToRawData - section.VirtualAddress; result := offset; end; end; {找到磁盘上某一个offset所映射的内存中的RVA} function TPE.Offset2RVA(dwOffset : DWORD): DWORD; var section : PImageSectionHeader; begin section := ImgOffset2Section(dwOffset); if section = nil then result := 0 else result := dwOffset + section.VirtualAddress - section.PointerToRawData; end; {返回PE文件加载到内存后,RVA地址所处的Section} function TPE.imgRVA2Section(dwRVA : DWORD) : PImageSectionHeader; var i : integer; begin for i:= 0 to imgNtHeaders.FileHeader.NumberOfSections - 1 do begin if ((dwRVA >= imgSectionHeaders[i].VirtualAddress) and (dwRVA <= (imgSectionHeaders[i].VirtualAddress + imgSectionHeaders[i].SizeOfRawData))) then begin result := @imgSectionHeaders[i]; exit; end; end; result := nil; end; {返回OFFSET地址在PE文件位于磁盘上的所落的Section} function TPE.ImgOffset2Section(dwOffset : DWORD): PImageSectionHeader; var i: integer; begin for i:=0 to imgNtHeaders.FileHeader.NumberOfSections - 1 do begin if ((dwOffset >= imgSectionHeaders[i].PointerToRawData) and (dwOffset < (imgSectionHeaders[i].PointerToRawData + imgSectionHeaders[i].SizeOfRawData))) then begin result := @ImgSectionHeaders[i]; exit; end; end; result := nil; end; {返回Offset地址在PE文件位于磁盘上所落Section的编号} function TPE.ImgOffset2SectionNum(dwOffset : DWORD): integer; var i: integer; begin for i:=0 to imgNtHeaders.FileHeader.NumberOfSections - 1 do begin if ((dwOffset >= imgSectionHeaders[i].PointerToRawData) and (dwOffset < (imgSectionHeaders[i].PointerToRawData + imgSectionHeaders[i].SizeOfRawData))) then begin result := i; exit; end; end; result := -1; end; {增加一个新的Section,可读/写, 包含已初始化数据.} function TPE.AddNewSection(szName: string; dwSize: DWORD): PImageSectionHeader; var i : integer; roffset : DWORD; rsize : DWORD; voffset : DWORD; vsize : DWORD; begin i := imgNtHeaders.FileHeader.NumberOfSections; rsize := PEAlign(dwSize, imgNtHeaders.OptionalHeader.FileAlignment); vsize := PEAlign(rsize, imgNtHeaders.OptionalHeader.SectionAlignment); roffset := PEAlign(imgSectionHeaders[i-1].PointerToRawData + imgSectionHeaders[i-1].SizeOfRawData, imgNtHeaders.OptionalHeader.FileAlignment); voffset := PEAlign(imgSectionHeaders[i-1].VirtualAddress + imgSectionHeaders[i-1].Misc.VirtualSize, imgNtHeaders.OptionalHeader.SectionAlignment); FillChar(imgSectionHeaders[i],sizeof(TImageSectionHeader),#0); imgSectionHeaders[i].PointerToRawData := roffset; imgSectionHeaders[i].VirtualAddress := voffset; imgSectionHeaders[i].SizeOfRawData := rsize; imgSectionHeaders[i].Misc.VirtualSize := vsize; imgSectionHeaders[i].Characteristics := $C0000040; CopyMemory(@imgSectionHeaders[i].Name[0],@szName[1],length(szName)); imgSections[i] := Pointer(GLobalAlloc(GMEM_FIXED or GMEM_ZEROINIT,rsize)); imgNtHeaders.FileHeader.NumberOfSections := imgNtHeaders.FileHeader.NumberOfSections + 1; result := @imgSectionHeaders[i]; end; {打开一个PE文件,按其格式分部分读入} procedure TPE.OpenFile(filename : string); var dwBytesRead : DWORD; hFile : THANDLE; sectionNum : DWORD; i : integer; firstSectionOffset : DWORD; dwOffset : DWORD; begin pMem := nil; hFile := CreateFile(PChar(filename),GENERIC_READ, FILE_SHARE_WRITE or FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0); if hFile = INVALID_HANDLE_VALUE then begin exit; end; dwFileSize := GetFileSize(hFile,0); if dwFileSize = 0 then begin CloseHandle(hFile); exit; end; pMem := Pointer(GlobalAlloc(GMEM_FIXED or GMEM_ZEROINIT, dwFileSize)); if dwFileSize = 0 then begin CloseHandle(hFile); exit; end; ReadFile(hFile,pMem^,dwFileSize,dwBytesRead,nil); CloseHandle(hFile); CopyMemory(@imgDosHeader,pMem,sizeof(IMAGE_DOS_HEADER)); dwDosStubSize := imgDosHeader._lfanew - sizeof(IMAGE_DOS_HEADER); dwDosStubOffset := sizeof(IMAGE_DOS_HEADER); getMem(pDosStub,dwDosStubSize); if (dwDosStubSize and $80000000) = $00000000 then begin copyMemory(pDosStub,pointer(DWORD(pMem) + dwDosStubOffset), dwDosStubSize); end; copyMemory(@imgNtHeaders,pointer(DWORD(pMem)+imgDosHeader._lfanew),sizeof(IMAGE_NT_HEADERS)); firstSectionOffset := imgDosHeader._lfanew + sizeof(IMAGE_NT_HEADERS); if imgDosHeader.e_magic <> IMAGE_DOS_SIGNATURE then begin GlobalFree(DWORD(pMem)); exit; end; SectionNum := imgNtHeaders.FileHeader.NumberOfSections; for i:=0 to SectionNum -1 do begin CopyMemory(@imgSectionHeaders[i],pointer(DWORD(pMem)+ firstSectionOffset + i * sizeof(IMAGE_SECTION_HEADER)),sizeof(IMAGE_SECTION_HEADER)); end; for i:=0 to SectionNum -1 do begin imgSections[i] := Pointer(GlobalAlloc(GMEM_FIXED or GMEM_ZEROINIT,PEAlign(imgSectionHeaders[i].SizeOfRawData,imgNtHeaders.OptionalHeader.FileAlignment))); copyMemory(imgSections[i],pointer(DWORD(pMem)+imgSectionHeaders[i].PointerToRawData), imgSectionHeaders[i].SizeOfRawData); end; if imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress <> 0 then begin dwOffset := RVA2Offset(imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress); copymemory(@imgTLSDirectory,pointer(DWORD(pMem) + dwOffset), sizeof(IMAGE_TLS_DIRECTORY32)); end; GlobalFree(DWORD(pMem)); end; procedure TPE.SaveFile(filename : string); var dwBytesWritten : DWORD; i : integer; dwRO_first_section : DWORD; sectionNum : DWORD; hFile : THANDLE; begin hFile := CreateFile(PChar(filename),GENERIC_WRITE, FILE_SHARE_WRITE or FILE_SHARE_READ, nil, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); if hFile = INVALID_HANDLE_VALUE then begin hFile := CreateFile(PChar(filename), GENERIC_WRITE, FILE_SHARE_WRITE or FILE_SHARE_READ, nil, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,0); if hFile = INVALID_HANDLE_VALUE then exit; end; AlignmentSections; i := imgNtHeaders.FileHeader.NumberOfSections; dwFileSize := imgSectionHeaders[i-1].PointerToRawData + imgSectionHeaders[i-1].SizeOfRawData; pMem := Pointer(GlobalAlloc(GMEM_FIXED or GMEM_ZEROINIT,dwFileSize)); if pMem = nil then begin CloseHandle(hFile); exit; end; copyMemory(pMem,@imgDosHeader,sizeof(IMAGE_DOS_HEADER)); if (dwDOSStubSize and $80000000) = $00000000 then copyMemory(pointer(DWORD(pMem) + dwDosStubOffset), pDosStub, dwDosStubSize); copyMemory(pointer(DWORD(pMem)+imgDosHeader._lfanew), @imgNtHeaders, sizeof(IMAGE_NT_HEADERS)); dwRO_first_section := imgDosHeader._lfanew + sizeof(IMAGE_NT_HEADERS); sectionNum := imgNtHeaders.FileHeader.NumberOfSections; for i:=0 to SectionNum - 1 do begin CopyMemory(pointer(DWORD(pMem) + dwRO_first_Section + i * sizeof(IMAGE_SECTION_HEADER)), @imgSectionHeaders[i],sizeof(IMAGE_SECTION_HEADER)); end; for i:=0 to SectionNum - 1 do begin CopyMemory(pointer(DWORD(pMem) + imgSectionHeaders[i].PointerToRawData), imgSections[i], ImgSectionHeaders[i].SizeOfRawData); end; SetFilePointer(hFile ,0, nil,FILE_BEGIN); writeFile(hFile,pMem^,dwFileSize, dwBytesWritten,nil); setFilePointer(hFile,dwFileSize, nil,FILE_BEGIN); setEndOfFile(hFile); CloseHandle(hFile); GlobalFree(DWORD(pMem)); end; {用来查找一段代码的标志位,在FuncCode指针开始的字符串中,查找一个DWORD的标志} function TPE.ReturnToBytePtr(FuncCode : pointer; findstr : DWORD): pointer; var tmpd : Pointer; begin asm pushad mov eax, FuncCode jmp @label1 @label0: inc eax @label1: mov ebx, [eax] cmp ebx, findstr jnz @label0 mov tmpd, eax popad end; result := tmpd; end; {把一段跳转指令加入到PE文件中,先建立一个新SECTION,然后把新输入表放入新区的 } { 起始地址,把一点代码放到输入表后.然后把跳转指令加入到新Section,跳回原来的程序,} {同时使用新的输入表来取代旧的输入表 } procedure TPE.CyrptFile; var ch_temp : Pointer; i : DWORD; imgSectionHeader : PImageSectionHeader; dwNewSectionSize : DWORD; dwCodeSize : DWORD; dwCodeOffset : DWORD; begin if (imgNTHeaders.FileHeader.Characteristics and IMAGE_FILE_DLL) = IMAGE_FILE_DLL then ITMaker := TImportTableMaker.Create(IMPORT_TABLE_DLL) else ITMaker := TImportTableMaker.Create(IMPORT_TABLE_EXE); ch_temp := Pointer(DWORD(ReturnToBytePtr(@InitPE,DYN_LOADER_START_MAGIC)) +4); dwCodeSize := DWORD(ReturnToBytePtr(@InitPE,DYN_LOADER_END_MAGIC)) - DWORD(ch_temp); dwCodeOffset := ITMaker.dwSize; dwNewSectionSize := dwCodeSize + ITMaker.dwSize; getmem(pNewSection,dwNewSectionSize); copymemory(Pointer(DWORD(pNewSection) + dwCodeOffset), ch_temp, dwCodeSize); imgSectionHeader := AddNewSection('.xxx',dwNewSectionSize); CopyData(imgSectionHeader.VirtualAddress); ITMaker.Build(imgSectionHeader.VirtualAddress); copyMemory(pNewSection,ITMaker.pMem, ITMaker.dwSize); copymemory(imgSections[imgNtHeaders.FileHeader.NumberOfSections-1], pNewSection, dwNewSectionSize); imgNtHeaders.OptionalHeader.AddressOfEntryPoint := imgSectionHeader.VirtualAddress + dwCodeOffset; imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress := imgSectionHeader.VirtualAddress; imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size := ITMaker.dwSize; imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress := 0; imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size := 0; SetSectionsWritePermission; freemem(pNewSection,dwNewSectionSize); ITMaker.Free; end; procedure TPE.CopyData(dwVirtualAddress : DWORD); var i : integer; APINum : integer; pData : Pointer; dwOffset : DWORD; len : longint; DataTable : TData; temp : byte; begin DataTable.dwReserved1 := $CCCCCCCC; if ((imgNtHeaders.FileHeader.Characteristics and IMAGE_FILE_DLL) = IMAGE_FILE_DLL) then DataTable.dwFileType := IMPORT_TABLE_DLL else DataTable.dwFileType := IMPORT_TABLE_EXE; DataTable.dwImageBase := imgNtHeaders.OptionalHeader.ImageBase; DataTable.dwOrgEntryPoint := imgNtHeaders.OptionalHeader.AddressOfEntryPoint; DataTable.dwImportVAddr := imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; if (imgNtHeaders.FileHeader.Characteristics and IMAGE_FILE_DLL) = IMAGE_FILE_DLL then begin DataTable.dwRelocationVAddr := imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; DataTable.dwRelocationSize := imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; end; pData := ReturnToBytePtr(pNewSection, DYN_LOADER_START_DATA1); if imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress <> 0 then begin CopyMemory(@DataTable.imgTLSDirectory,@imgTLSDirectory,sizeof(IMAGE_TLS_DIRECTORY32)); dwOffset := DWORD(pData) - DWORD(pNewSection); dwOffset := dwOffset + sizeof(DataTable) - sizeof(IMAGE_TLS_DIRECTORY32); imgNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress := dwVirtualAddress + dwOffset; end; CopyMemory(pData,@DataTable, sizeof(TData)); dwOffset := sizeof(TDATA); i := 0; APINum := 0; temp := 0; repeat len := Length(szWindowsAPIs[i]) + 1; CopyMemory(Pointer(DWORD(pData) + dwOffset),@szWindowsAPIs[i][1],len); dwOffset := dwOffset + len; repeat i := i + 1; if szWindowsAPIs[i] <> '' then begin len := length(szWindowsAPIs[i]) + 1; CopyMemory(Pointer(DWORD(pData) + dwOffset), @szWindowsAPIs[i][1],len); dwOffset := dwOffset + len; APINum := APINum + 1; end else begin CopyMemory(Pointer(DWORD(pData) + dwOffset), @temp, 1); dwOffset := dwOffset + 1; end; until szWindowsAPIs[i] = ''; i := i + 1; until szWindowsAPIs[i] = ''; end; procedure TPE.SetSectionsWritePermission; var i : integer; begin for i:=0 to imgNTHeaders.FileHeader.NumberOfSections - 1 do imgSectionHeaders[i].Characteristics := $C0000040; end; {******************************************************************************} constructor TImportTableMaker.Create(iType : integer); begin ImportTable := TList.Create; init(iType); dwSize := GetIATSize; GetMem(pMem,dwSize); end; destructor TImportTableMaker.Destroy; var imgImport : PImageImport; begin while ImportTable.Count > 0 do begin imgImport := PImageImport(ImportTable.Items[0]); while imgImport.ThunksList.Count > 0 do begin freeMem(imgImport.ThunksList.Items[0],32); imgImport.ThunksList.Delete(0); end; imgImport.ThunksList.Free; dispose(imgImport); ImportTable.Delete(0); end; ImportTable.Free; FreeMem(pMem,dwSize); end; procedure TImportTableMaker.Init(iType : integer); var i : integer; IATString : pointer; imgImport : PImageImport; imgThunk : PChar; function IsStringsEnd(inx : integer) : boolean; begin result := false; case iType of IMPORT_TABLE_EXE: begin if szIATEXEStrings[inx] = '' then result := true; end; IMPORT_TABLE_DLL: begin if szIATDLLStrings[inx] = '' then result := true; end; end; end; begin i := 0; repeat New(imgImport); imgImport.ThunksList := TList.Create; case iType of IMPORT_TABLE_EXE: copyMemory(@imgImport.szLibrary[0], @szIATEXEStrings[i][1],32); IMPORT_TABLE_DLL: copyMemory(@imgImport.szLibrary[0], @szIATDLLStrings[i][1],32); end; repeat i := i + 1; if not IsStringsEnd(i) then begin getMem(imgThunk,32); fillchar(imgTHunk^,32,#0); case iType of IMPORT_TABLE_EXE: copyMemory(imgThunk,@szIATEXEStrings[i][1],32); IMPORT_TABLE_DLL: copyMemory(imgThunk,@szIATDLLStrings[i][1],32); end; imgImport.ThunksList.Add(imgThunk); end; until IsStringsEnd(i); ImportTable.Add(imgImport); i := i + 1; until IsStringsEnd(i); end; function TImportTableMaker.GetIATSize : DWORD; var i : integer; j : integer; dwDLLNum : DWORD; dwFuncNum : DWORD; dwszDLLSize : DWORD; dwszFuncSize : DWORD; dwImportSize : DWORD; imgImport : PImageImport; begin dwDLLNum := 0; dwFuncNum := 0; dwszDLLSize := 0; dwszFuncSize := 0; dwImportSize := 0; for i:= 0 to ImportTable.Count - 1 do begin imgImport := ImportTable.Items[i]; dwszDLLSize := dwszDLLSize + strlen(imgimport.szLibrary) + 1; for j:=0 to imgImport.ThunksList.Count - 1 do begin dwszFuncSize := dwszFuncSize + 2 + strlen(PChar(imgimport.ThunksList.Items[j])) + 1; dwFuncNum := dwFuncNum + 1; end; dwFuncNum := dwFuncNum + 1; dwDLLNum := dwDLLNum + 1; end; dwDLLNum := dwDLLNum + 1; dwImportSize := dwDLLnum * 20 + dwFuncNum * 4 + dwSzDLLSize + dwszFuncSize; result := dwImportSize; end; procedure TImportTableMaker.Build(baseRVA : DWORD); var i : integer; j : integer; pITBaseRVA : DWORD; temp : DWORD; dwDLLNum : DWORD; dwDLLName : DWORD; dwDLLFirst : DWORD; dwszDLLSize : DWORD; dwIIDNum : DWORD; dwFunNum : DWORD; dwFunFirst : DWORD; dwszFuncSize : DWORD; dwFirstThunk : DWORD; dwImportSize : DWORD; imgImport : PImageImport; importDesc : TImageImportDecriptor; begin pITBaseRVA := baseRVA; importDesc.Union.OriginalFirstThunk := 0; importDesc.TimeDateStamp := 0; importDesc.ForwarderChain := 0; importDesc.Name := 0; importDesc.FirstThunk := 0; dwDLLNum := 0; dwDLLName := 0; dwDLLFirst := 0; dwszDLLSize := 0; dwIIDNum := 0; dwFunNum := 0; dwFunFirst := 0; dwszFuncSize := 0; dwFirstThunk := 0; dwImportSize := 0; for i:= 0 to importTable.Count -1 do begin imgImport := PImageImport(importTable.Items[i]); dwszDLLSize := dwszDLLSize + strlen(imgImport.szLibrary) + 1; for j:= 0 to imgImport.ThunksList.Count - 1 do begin dwszFuncSize := dwszFuncSize + 2 + strlen(PChar(imgImport.ThunksList.Items[j])) + 1; dwFunNum := dwFunNum + 1; end; dwFunNum := dwFunNum + 1; dwDLLNum := dwDLLNum + 1; end; dwDLLNum := dwDLLNum + 1; dwImportSize := dwDLLNum * 20 + dwFunNum * 4 + dwszDLLSize + dwszFuncSize; FillMemory(pMem,dwImportSize, 0); dwFirstThunk := dwDLLNum * 20; dwDLLFirst := dwDLLNum * 20 + dwFunNum * 4; dwFunFirst := dwDLLNum * 20 + dwFunNum * 4 + dwszDLLSize; for i := 0 to importTable.Count - 1 do begin imgImport := importTable.Items[i]; importDesc.Name := pITBaseRVA + dwDLLFirst; importDesc.FirstThunk := pITBaseRVA + dwFirstThunk; CopyMemory(Pointer(DWORD(pMem) + dwIIDNum * sizeof(IMAGE_IMPORT_DESCRIPTOR)),@importDesc,sizeof(IMAGE_IMPORT_DESCRIPTOR)); CopyMemory(Pointer(DWORD(pMem) + dwDLLFirst),@imgImport.szLibrary[0],strlen(imgImport.szLibrary) + 1); for j:=0 to imgimport.ThunksList.Count - 1 do begin temp := pITBaseRVA + dwFunFirst; CopyMemory(Pointer(DWORD(pMem) + dwFirstThunk),@temp,4); CopyMemory(Pointer(DWORD(pMem) + dwFunFirst + 2), PChar(imgImport.ThunksList.Items[j]),strlen(imgImport.ThunksList.Items[j]) + 1); dwFunFirst := dwFunFirst + 2 + strlen(imgImport.ThunksList.Items[j]) + 1; dwFirstThunk := dwFirstThunk + 4; end; temp := 0; CopyMemory(Pointer(DWORD(pMem) + dwFirstThunk),@temp, 4); dwFirstThunk := dwFirstTHunk + 4; dwDLLFirst := dwDLLFirst + strlen(imgImport.szLibrary) + 1; dwIIDNum := dwIIDNum + 1; end; importDesc.Name := 0; importDesc.FirstThunk := 0; CopyMemory(Pointer(DWORD(pMem)+dwIIDNum * sizeof(IMAGE_IMPORT_DESCRIPTOR)), @importDesc,sizeof(IMAGE_IMPORT_DESCRIPTOR)); end; end. {************************************************************************************************************} PEint单元 unit PEInit; interface uses windows; procedure InitPE; stdcall; implementation {一段要嵌入到PE文件中的指令} {标志位的作用是用来找到找到从开始标志位后到结束标志位之间的指令大小及开始地址} {这段代码如果用在VC中,则要设置链接选项/INCREMENTAL LINK OFF} procedure InitPE; stdcall; begin asm {开始标志位} DB $A9 DB $51 DB $DE DB $C0 @Main_0: pushad; call @Main_1 @Main_1: pop ebp sub ebp, offset @main_1 {**********************支持DLL,OCX等****************************************} @_support_dll_0: jmp @_support_dll_1 {// nop; nop; // in the secon time OEP 第一次加载后,} {这句会被改成 nop; nop;在DLL被卸载的时候,会直接调用jmp @_support_dll_2} jmp @_support_dll_2 @_support_dll_1: test [ebp + @_p_dwFileType],0001h // IMPORT_TABLE_DLL jz @_no_dll_pe_file_0 mov eax,[esp+24h] mov ebx,[esp+34h] cmp eax,ebx ja @_no_dll_pe_file_0 cmp word ptr [eax], IMAGE_DOS_SIGNATURE jne @_no_dll_pe_file_0 mov [ebp + @_RO_dwImageBase],eax @_no_dll_pe_file_0: // 获取输入表中的LoadLibrary 和 GetProcAddress来建立函数跳转表 mov eax, DWORD PTR [ebp + @_RO_dwImageBase] add eax, [eax + 03Ch] add eax, 080h mov ecx, [eax] add ecx, [ebp + @_RO_dwImageBase] add ecx, 010h mov eax, [ecx] add eax, [ebp + @_RO_dwImageBase] mov ebx, [eax] mov [ebp + @_p_LoadLibrary],ebx add eax, 04h mov ebx, [eax] mov [ebp + @_p_GetProcAddress], ebx call @_api_load // 加载插入功能代码所需要的DLL和函数跳转表 //***************************功能性代码*************************************** push MB_OK or MB_ICONINFORMATION lea eax,[ebp + @_p_szCaption] push eax lea eax,[ebp + @_p_szText] push eax push 0000h call @_jmp_MessageBox //**************************************************************************** {将在内存中的PE文件的NTHeader头改为可读写} mov edi, [ebp + @_RO_dwImageBase] add edi, [edi + 03Ch] lea eax, [ebp + @_p_ptempbuffer] push eax push PAGE_READWRITE push [edi + 54h] push [ebp + @_RO_dwImageBase] call @_jmp_VirtualProtect call @_it_fixup // 为被插入的程序建立内存中的输入表 {判断是否是DLL} mov edi,[ebp + @_RO_dwImageBase] add edi,[edi+03Ch] mov ax,word ptr [edi+016h] test ax,IMAGE_FILE_DLL jz @_no_dll_pe_file_1 call @_reloc_fixup // 为被捆绑的程序修正重定位表.因为EXE一般不需要,只在DLL的时候需要。 mov ax,9090h // 为DLL建立卸载时候的入口点 mov word ptr [ebp + @_support_dll_0],ax @_no_dll_pe_file_1: @_support_dll_2: //--------- 利用SEH异常转入程序入口点 --------- mov eax, [ebp + @_RO_dwImageBase] add eax, DWORD PTR [ebp + @_RO_dwOrgEntryPoint] mov DWORD PTR [esp + 10h], eax lea eax, [ebp + @_except_handler1_oep_jump] mov DWORD PTR [esp + 1ch],eax popad push eax xor eax,eax push DWORD PTR FS:[0] mov DWORD PTR FS:[0], ESP {注册完异常处理回调函数,开始调用INT 3 触发异常,异常发生后,} {线程的栈被保护起来,原来存有真正OEP地址的EBX也被保存起来} DB $CC DB $CC DB $CC DB $CC {eax中返回字符串的长度} @_strlen: push ebp mov ebp,esp push ecx push esi push ebx mov esi,[ebp+08h] mov ecx,255// -> Length xor ebx,ebx @_strlenloop: lods byte ptr ds:[esi] cmp al,00h jz @_endbufstrlen inc ebx loop @_strlenloop @_endbufstrlen: mov eax,ebx inc eax pop ebx pop esi pop ecx mov esp,ebp pop ebp ret {*********************为被插入代码的程序修正重定位表*******************************} @_reloc_fixup: mov eax,[ebp + @_RO_dwImageBase] mov edx,eax mov ebx,eax add ebx,[ebx+3Ch] mov ebx,[ebx+034h] sub edx,ebx je @_reloc_fixup_end mov ebx,[ebp + @_p_dwRelocationVirtualAddress] test ebx,ebx jz @_reloc_fixup_end add ebx,eax @_reloc_fixup_block: mov eax,[ebx+004h] test eax,eax jz @_reloc_fixup_end lea ecx,[eax-008h] shr ecx,001h lea edi,[ebx+008h] @_reloc_fixup_do_entry: movzx eax,word ptr [edi] push edx mov edx,eax shr eax,00Ch mov esi,[ebp + @_RO_dwImageBase] and dx,00FFFh add esi,[ebx] add esi,edx pop edx @_reloc_fixup_HIGH: dec eax jnz @_reloc_fixup_LOW mov eax,edx shr eax,010h //HIWORD(Delta) jmp @_reloc_fixup_LOW_fixup @_reloc_fixup_LOW: dec eax jnz @_reloc_fixup_HIGHLOW movzx eax,dx //LOWORD(Delta) @_reloc_fixup_LOW_fixup: add word ptr [esi],ax jmp @_reloc_fixup_next_entry @_reloc_fixup_HIGHLOW: dec eax jnz @_reloc_fixup_next_entry add [esi],edx @_reloc_fixup_next_entry: inc edi inc edi //Entry++ loop @_reloc_fixup_do_entry @_reloc_fixup_next_base: add ebx,[ebx+004h] //ImageBaseRelocation + ImageBaseRelocation.SizeOfBlock jmp @_reloc_fixup_block @_reloc_fixup_end: ret {**************为捆绑的程序在内存中建立IAT表*********************************} {1. 加载输入表中对应的DLL。 } {2. 加载输入表里对应DLL的输入函数。 } {3. 讲函数的内存地址放入输入表的FirstThunk里,建立IAT表 } @_it_fixup: mov ebx,[ebp + @_p_dwImportVirtualAddress] test ebx,ebx jz @_it_fixup_end mov esi,[ebp + @_RO_dwImageBase] add ebx,esi @_it_fixup_get_lib_address_loop: mov eax,[ebx+00Ch] test eax,eax jz @_it_fixup_end mov ecx,[ebx+010h] add ecx,esi mov [ebp + @_p_dwThunk],ecx mov ecx,[ebx] test ecx,ecx jnz @_it_fixup_table mov ecx,[ebx + 010h] @_it_fixup_table: add ecx,esi mov [ebp + @_p_dwHintName],ecx add eax,esi push eax mov eax,offset @_p_LoadLibrary call [ebp+eax] test eax,eax jz @_it_fixup_end mov edi,eax @_it_fixup_get_proc_address_loop: mov ecx,[ebp + @_p_dwHintName] mov edx,[ecx] test edx,edx jz @_it_fixup_next_module test edx,80000000h jz @_it_fixup_by_name and edx,07FFFFFFFh jmp @_it_fixup_get_addr @_it_fixup_by_name: add edx,esi inc edx inc edx @_it_fixup_get_addr: push edx push edi mov eax,offset @_p_GetProcAddress call [ebp+eax] mov ecx,[ebp + @_p_dwThunk] mov [ecx],eax add dword ptr [ebp + @_p_dwThunk],004h add dword ptr [ebp + @_p_dwHintName],004h jmp @_it_fixup_get_proc_address_loop @_it_fixup_next_module: add ebx,014h jmp @_it_fixup_get_lib_address_loop @_it_fixup_end: ret {***************加载动态库,然后建立其输入函数的跳转表***********************} @_api_load: lea edi, [ebp + @_p_szKernel32] lea ebx, [ebp + @_p_GetModuleHandle] lea ecx, [ebp + @_jmp_GetModuleHandle] add ecx, 02h @_api_get_lib_address_loop: push ecx push edi mov eax, offset @_p_LoadLibrary call [ebp + eax] pop ecx mov esi, eax push edi call @_strlen add esp, 04h add edi, eax @_api_get_proc_address_loop: push ecx push edi push esi mov eax, offset @_p_GetProcAddress call [ebp + eax] pop ecx mov [ebx], eax mov [ecx], ebx add ebx, 04h add ecx, 06h push edi call @_strlen add esp, 04h add edi, eax mov al, byte ptr [edi] test al,al jnz @_api_get_proc_address_loop inc edi mov al, byte ptr [edi] test al, al jnz @_api_get_lib_address_loop ret {SEH异常处理回调函数的有四个参数,分别为ExceptionRecord, SEH, Context, } {DispatcherContext,先压入下一个指令的地址,然后根据被压入栈的这四个参数中 } {Context的 |
四、测试。
可以对EXE文件和DLL文件进行代码插入。当被插入的EXE文件或DLL文件运行或被调用的时候,都会先跳出一个对话框:
然后再运行或调用被插入的EXE或DLL文件。
http://blog.csdn.net/suiyunonghen/article/details/3860206