CSDN博客

img 3150379

从内存中加载并运行exe

发表于2008/10/2 14:42:00  966人阅读

  1. {配合anskya的AnyWhereFileToPas效果不错}
  2. { ******************************************************* }
  3. { *                 从内存中加载并运行exe               * }
  4. { ******************************************************* }
  5. { * 参数:                                                }
  6. { * Buffer: 内存中的exe地址                               }
  7. { * Len: 内存中exe占用长度                                }
  8. { * CmdParam: 命令行参数(不包含exe文件名的剩余命令行参数)}
  9. { * ProcessId: 返回的进程Id                               }
  10. { * 返回值: 如果成功则返回进程的Handle(ProcessHandle),   }
  11. {            如果失败则返回INVALID_HANDLE_VALUE           }
  12. { ******************************************************* }
  13. unit PEUnit;
  14. interface
  15. uses windows;
  16. function MemExecute(const ABuffer; Len: Integer; CmdParam: stringvar ProcessId: Cardinal): Cardinal;
  17. implementation
  18. {.$R ExeShell.res}   // 外壳程序模板(98下使用)
  19. type
  20. TImageSectionHeaders = array [0..0of TImageSectionHeader;
  21. PImageSectionHeaders = ^TImageSectionHeaders;
  22. { 计算对齐后的大小 }
  23. function GetAlignedSize(Origin, Alignment: Cardinal): Cardinal;
  24. begin
  25. result := (Origin + Alignment - 1div Alignment * Alignment;
  26. end;
  27. { 计算加载pe并对齐需要占用多少内存,未直接使用OptionalHeader.SizeOfImage作为结果是因为据说有的编译器生成的exe这个值会填0 }
  28. function CalcTotalImageSize(MzH: PImageDosHeader; FileLen: Cardinal; peH: PImageNtHeaders;
  29.     peSecH: PImageSectionHeaders): Cardinal;
  30. var
  31. i: Integer;
  32. begin
  33. {计算pe头的大小}
  34. result := GetAlignedSize(PeH.OptionalHeader.SizeOfHeaders, PeH.OptionalHeader.SectionAlignment);
  35. {计算所有节的大小}
  36. for i := 0 to peH.FileHeader.NumberOfSections - 1 do
  37.     if peSecH[i].PointerToRawData + peSecH[i].SizeOfRawData > FileLen then // 超出文件范围
  38.     begin
  39.       result := 0;
  40.       exit;
  41.     end
  42.     else if peSecH[i].VirtualAddress <> 0 then //计算对齐后某节的大小
  43.       if peSecH[i].Misc.VirtualSize <> 0 then
  44.         result := GetAlignedSize(peSecH[i].VirtualAddress + peSecH[i].Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment)
  45.       else
  46.         result := GetAlignedSize(peSecH[i].VirtualAddress + peSecH[i].SizeOfRawData, PeH.OptionalHeader.SectionAlignment)
  47.     else if peSecH[i].Misc.VirtualSize < peSecH[i].SizeOfRawData then
  48.       result := result + GetAlignedSize(peSecH[i].SizeOfRawData, peH.OptionalHeader.SectionAlignment)
  49.     else
  50.       result := result + GetAlignedSize(peSecH[i].Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment);
  51. end;
  52. { 加载pe到内存并对齐所有节 }
  53. function AlignPEToMem(const Buf; Len: Integer; var PeH: PImageNtHeaders;
  54.     var PeSecH: PImageSectionHeaders; var Mem: Pointer; var ImageSize: Cardinal): Boolean;
  55. var
  56. SrcMz: PImageDosHeader;            // DOS头
  57. SrcPeH: PImageNtHeaders;           // PE头
  58. SrcPeSecH: PImageSectionHeaders;   // 节表
  59. i: Integer;
  60. l: Cardinal;
  61. Pt: Pointer;
  62. begin
  63. result := false;
  64. SrcMz := @Buf;
  65. if Len < sizeof(TImageDosHeader) then exit;
  66. if SrcMz.e_magic <> IMAGE_DOS_SIGNATURE then exit;
  67. if Len < SrcMz._lfanew+Sizeof(TImageNtHeaders) then exit;
  68. SrcPeH := pointer(Integer(SrcMz)+SrcMz._lfanew);
  69. if (SrcPeH.Signature <> IMAGE_NT_SIGNATURE) then exit;
  70. if (SrcPeH.FileHeader.Characteristics and IMAGE_FILE_DLL <> 0or
  71.       (SrcPeH.FileHeader.Characteristics and IMAGE_FILE_EXECUTABLE_IMAGE = 0)
  72.       or (SrcPeH.FileHeader.SizeOfOptionalHeader <> SizeOf(TImageOptionalHeader)) then exit;
  73. SrcPeSecH := Pointer(Integer(SrcPeH)+SizeOf(TImageNtHeaders));
  74. ImageSize := CalcTotalImageSize(SrcMz, Len, SrcPeH, SrcPeSecH);
  75. if ImageSize = 0 then
  76.     exit;
  77. Mem := VirtualAlloc(nil, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 分配内存
  78. if Mem <> nil then
  79. begin
  80.     // 计算需要复制的PE头字节数
  81.     l := SrcPeH.OptionalHeader.SizeOfHeaders;
  82.     for i := 0 to SrcPeH.FileHeader.NumberOfSections - 1 do
  83.       if (SrcPeSecH[i].PointerToRawData <> 0and (SrcPeSecH[i].PointerToRawData < l) then
  84.         l := SrcPeSecH[i].PointerToRawData;
  85.     Move(SrcMz^, Mem^, l);
  86.     PeH := Pointer(Integer(Mem) + PImageDosHeader(Mem)._lfanew);
  87.     PeSecH := Pointer(Integer(PeH) + sizeof(TImageNtHeaders));
  88.     Pt := Pointer(Cardinal(Mem) + GetAlignedSize(PeH.OptionalHeader.SizeOfHeaders, PeH.OptionalHeader.SectionAlignment));
  89.     for i := 0 to PeH.FileHeader.NumberOfSections - 1 do
  90.     begin
  91.       // 定位该节在内存中的位置
  92.       if PeSecH[i].VirtualAddress <> 0 then
  93.         Pt := Pointer(Cardinal(Mem) + PeSecH[i].VirtualAddress);
  94.       if PeSecH[i].SizeOfRawData <> 0 then
  95.       begin
  96.         // 复制数据到内存
  97.         Move(Pointer(Cardinal(SrcMz) + PeSecH[i].PointerToRawData)^, pt^, PeSecH[i].SizeOfRawData);
  98.         if peSecH[i].Misc.VirtualSize < peSecH[i].SizeOfRawData then
  99.           pt := pointer(Cardinal(pt) + GetAlignedSize(PeSecH[i].SizeOfRawData, PeH.OptionalHeader.SectionAlignment))
  100.         else
  101.           pt := pointer(Cardinal(pt) + GetAlignedSize(peSecH[i].Misc.VirtualSize, peH.OptionalHeader.SectionAlignment));
  102.         // pt 定位到下一节开始位置
  103.       end
  104.       else
  105.         pt := pointer(Cardinal(pt) + GetAlignedSize(PeSecH[i].Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment));
  106.     end;
  107.     result := True;
  108. end;
  109. end;
  110. type
  111. TVirtualAllocEx = function (hProcess: THandle; lpAddress: Pointer;
  112.                                   dwSize, flAllocationType: DWORD; flProtect: DWORD): Pointer; stdcall;
  113. var
  114. MyVirtualAllocEx: TVirtualAllocEx = nil;
  115. function IsNT: Boolean;
  116. begin
  117. result := Assigned(MyVirtualAllocEx);
  118. end;
  119. { 生成外壳程序命令行 }
  120. function PrepareShellExe(CmdParam: string; BaseAddr, ImageSize: Cardinal): string;
  121. var
  122. r, h, sz: Cardinal;
  123. p: Pointer;
  124. fid, l: Integer;
  125. buf: Pointer;
  126. peH: PImageNtHeaders;
  127. peSecH: PImageSectionHeaders;
  128. begin
  129. if IsNT then
  130. { NT 系统下直接使用自身程序作为外壳进程 }
  131.     result := ParamStr(0)+CmdParam
  132. else begin
  133. // 由于98系统下无法重新分配外壳进程占用内存,所以必须保证运行的外壳程序能容纳目标进程并且加载地址一致
  134. // 此处使用的方法是从资源中释放出一个事先建立好的外壳程序,然后通过修改其PE头使其运行时能加载到指定地址并至少能容纳目标进程
  135.     r := FindResource(HInstance, 'SHELL_EXE', RT_RCDATA);
  136.     h := LoadResource(HInstance, r);
  137.     p := LockResource(h);
  138.     l := SizeOfResource(HInstance, r);
  139.     GetMem(Buf, l);
  140.     Move(p^, Buf^, l);   // 读到内存
  141.     FreeResource(h);
  142.     peH := Pointer(Integer(Buf) + PImageDosHeader(Buf)._lfanew);
  143.     peSecH := Pointer(Integer(peH) + sizeof(TImageNtHeaders));
  144.     peH.OptionalHeader.ImageBase := BaseAddr;    // 修改PE头重的加载基址
  145.     if peH.OptionalHeader.SizeOfImage < ImageSize then // 目标比外壳大,修改外壳程序运行时占用的内存
  146.     begin
  147.       sz := Imagesize - peH.OptionalHeader.SizeOfImage;
  148.       Inc(peH.OptionalHeader.SizeOfImage, sz);    // 调整总占用内存数
  149.       Inc(peSecH[peH.FileHeader.NumberOfSections-1].Misc.VirtualSize, sz);   // 调整最后一节占用内存数
  150.     end;
  151.     // 生成外壳程序文件名, 为本程序改后缀名得到的
  152.     // 由于不想 uses SysUtils (一旦 use 了程序将增大80K左右), 而且偷懒,所以只支持最多运行11个进程,后缀名为.dat, .da0~.da9
  153.     result := ParamStr(0);
  154.     result := copy(result, 1, length(result) - 4) + '.dat';
  155.     r := 0;
  156.     while r < 10 do
  157.     begin
  158.       fid := CreateFile(pchar(result), GENERIC_READ or GENERIC_WRITE, 0nil, Create_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  159.       if fid < 0 then
  160.       begin
  161.         result := copy(result, 1, length(result)-3)+'da'+Char(r+Byte('0'));
  162.         inc(r);
  163.       end
  164.       else begin
  165.         //SetFilePointer(fid, Imagesize, nil, 0);
  166.         //SetEndOfFile(fid);
  167.         //SetFilePointer(fid, 0, nil, 0);
  168.         WriteFile(fid, Buf^, l, h, nil); // 写入文件
  169.         CloseHandle(fid);
  170.         break;
  171.       end;
  172.     end;
  173. //    result := result + CmdParam; // 生成命令行
  174.     result   := '"'+result+'"'+CmdParam ;
  175.     FreeMem(Buf);
  176. end;
  177. end;
  178. { 是否包含可重定向列表 }
  179. function HasRelocationTable(peH: PImageNtHeaders): Boolean;
  180. begin
  181. result := (peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> 0)
  182.       and (peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size <> 0);
  183. end;
  184. type
  185. PImageBaseRelocation= ^TImageBaseRelocation;
  186. TImageBaseRelocation = packed record
  187.     VirtualAddress: cardinal;
  188.     SizeOfBlock: cardinal;
  189. end;
  190. { 重定向PE用到的地址 }
  191. procedure DoRelocation(peH: PImageNtHeaders; OldBase, NewBase: Pointer);
  192. var
  193. Delta: Cardinal;
  194. p: PImageBaseRelocation;
  195. pw: PWord;
  196. i: Integer;
  197. begin
  198. Delta := Cardinal(NewBase) - peH.OptionalHeader.ImageBase;
  199. p := pointer(cardinal(OldBase) + peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
  200. while (p.VirtualAddress + p.SizeOfBlock <> 0do
  201. begin
  202.     pw := pointer(Integer(p) + Sizeof(p^));
  203.     for i := 1 to (p.SizeOfBlock - Sizeof(p^)) div 2 do
  204.     begin
  205.       if pw^ and $F000 = $3000 then
  206.         Inc(PCardinal(Cardinal(OldBase) + p.VirtualAddress + (pw^ and $0FFF))^, Delta);
  207.       inc(pw);
  208.     end;
  209.     p := Pointer(pw);
  210. end;
  211. end;
  212. type
  213. TZwUnmapViewOfSection = function (Handle, BaseAdr: Cardinal): Cardinal; stdcall;
  214. { 卸载原外壳占用内存 }
  215. function UnloadShell(ProcHnd, BaseAddr: Cardinal): Boolean;
  216. var
  217. M: HModule;
  218. ZwUnmapViewOfSection: TZwUnmapViewOfSection;
  219. begin
  220. result := False;
  221. m := LoadLibrary('ntdll.dll');
  222. if m <> 0 then
  223. begin
  224.     ZwUnmapViewOfSection := GetProcAddress(m, 'ZwUnmapViewOfSection');
  225.     if assigned(ZwUnmapViewOfSection) then
  226.       result := (ZwUnmapViewOfSection(ProcHnd, BaseAddr) = 0);
  227.     FreeLibrary(m);
  228. end;
  229. end;
  230. { 创建外壳进程并获取其基址、大小和当前运行状态 }
  231. function CreateChild(Cmd: stringvar Ctx: TContext; var ProcHnd, ThrdHnd, ProcId, BaseAddr, ImageSize: Cardinal): Boolean;
  232. var
  233. si: TStartUpInfo;
  234. pi: TProcessInformation;
  235. Old: Cardinal;
  236. MemInfo: TMemoryBasicInformation;
  237. p: Pointer;
  238. begin
  239. FillChar(si, Sizeof(si), 0);
  240. FillChar(pi, SizeOf(pi), 0);
  241. si.cb := sizeof(si);
  242. result := CreateProcess(nil, PChar(Cmd), nilnil, False, Create_SUSPENDED, nilnil, si, pi); // 以挂起方式运行进程
  243. if result then
  244. begin
  245.     ProcHnd := pi.hProcess;
  246.     ThrdHnd := pi.hThread;
  247.     ProcId := pi.dwProcessId;
  248.     { 获取外壳进程运行状态,[ctx.Ebx+8]内存处存的是外壳进程的加载基址,ctx.Eax存放有外壳进程的入口地址 }
  249.     ctx.ContextFlags := CONTEXT_FULL;
  250.     GetThreadContext(ThrdHnd, ctx);
  251.     ReadProcessMemory(ProcHnd, Pointer(ctx.Ebx+8), @BaseAddr, SizeOf(Cardinal), Old); // 读取加载基址
  252.     p := Pointer(BaseAddr);
  253.     { 计算外壳进程占有的内存 }
  254.     while VirtualQueryEx(ProcHnd, p, MemInfo, Sizeof(MemInfo)) <> 0 do
  255.     begin
  256.       if MemInfo.State = MEM_FREE then
  257.         break;
  258.       p := Pointer(Cardinal(p) + MemInfo.RegionSize);
  259.     end;
  260.     ImageSize := Cardinal(p) - Cardinal(BaseAddr);
  261. end;
  262. end;
  263. { 创建外壳进程并用目标进程替换它然后执行 }
  264. function AttachPE(CmdParam: string; peH: PImageNtHeaders; peSecH: PImageSectionHeaders;
  265.     Ptr: Pointer; ImageSize: Cardinal; var ProcId: Cardinal): Cardinal;
  266. var
  267. s: string;
  268. Addr, Size: Cardinal;
  269. ctx: TContext;
  270. Old: Cardinal;
  271. p: Pointer;
  272. Thrd: Cardinal;
  273. begin
  274. result := INVALID_HANDLE_VALUE;
  275. s := PrepareShellExe(CmdParam, peH.OptionalHeader.ImageBase, ImageSize);
  276. if CreateChild(s, ctx, result, Thrd, ProcId, Addr, Size) then
  277. begin
  278.     p := nil;
  279.     if (peH.OptionalHeader.ImageBase = Addr) and (Size >= ImageSize) then // 外壳进程可以容纳目标进程并且加载地址一致
  280.     begin
  281.       p := Pointer(Addr);
  282.       VirtualProtectEx(result, p, Size, PAGE_EXECUTE_READWRITE, Old);
  283.     end
  284.     else if IsNT then // 98 下失败
  285.     begin
  286.       if UnloadShell(result, Addr) then // 卸载外壳进程占有内存
  287.         // 重新按目标进程加载基址和大小分配内存
  288.         p := MyVirtualAllocEx(Result, Pointer(peH.OptionalHeader.ImageBase), ImageSize, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  289.       if (p = niland hasRelocationTable(peH) then // 分配内存失败并且目标进程支持重定向
  290.       begin
  291.         // 按任意基址分配内存
  292.         p := MyVirtualAllocEx(result, nil, ImageSize, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  293.         if p <> nil then
  294.           DoRelocation(peH, Ptr, p); // 重定向
  295.       end;
  296.     end;
  297.     if p <> nil then
  298.     begin
  299.       WriteProcessMemory(Result, Pointer(ctx.Ebx+8), @p, Sizeof(DWORD), Old); // 重置目标进程运行环境中的基址
  300.       peH.OptionalHeader.ImageBase := Cardinal(p);
  301.       if WriteProcessMemory(Result, p, Ptr, ImageSize, Old) then // 复制PE数据到目标进程
  302.       begin
  303.         ctx.ContextFlags := CONTEXT_FULL;
  304.         if Cardinal(p) = Addr then
  305.           ctx.Eax := peH.OptionalHeader.ImageBase + peH.OptionalHeader.AddressOfEntryPoint // 重置运行环境中的入口地址
  306.         else
  307.           ctx.Eax := Cardinal(p) + peH.OptionalHeader.AddressOfEntryPoint;
  308.         SetThreadContext(Thrd, ctx); // 更新运行环境
  309.         ResumeThread(Thrd);           // 执行
  310.         CloseHandle(Thrd);
  311.       end
  312.       else begin // 加载失败,杀掉外壳进程
  313.         TerminateProcess(Result, 0);
  314.         CloseHandle(Thrd);
  315.         CloseHandle(Result);
  316.         Result := INVALID_HANDLE_VALUE;
  317.       end;
  318.     end
  319.     else begin // 加载失败,杀掉外壳进程
  320.       TerminateProcess(Result, 0);
  321.       CloseHandle(Thrd);
  322.       CloseHandle(Result);
  323.       Result := INVALID_HANDLE_VALUE;
  324.     end;
  325. end;
  326. end;
  327. function MemExecute(const ABuffer; Len: Integer; CmdParam: stringvar ProcessId: Cardinal): Cardinal;
  328. var
  329. peH: PImageNtHeaders;
  330. peSecH: PImageSectionHeaders;
  331. Ptr: Pointer;
  332. peSz: Cardinal;
  333. begin
  334. result := INVALID_HANDLE_VALUE;
  335. if alignPEToMem(ABuffer, Len, peH, peSecH, Ptr, peSz) then
  336. begin
  337.     result := AttachPE(CmdParam, peH, peSecH, Ptr, peSz, ProcessId);
  338.     VirtualFree(Ptr, peSz, MEM_DECOMMIT);
  339.     //VirtualFree(Ptr, 0, MEM_RELEASE);
  340. end;
  341. end;
  342. initialization
  343. MyVirtualAllocEx := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'VirtualAllocEx');
  344. end
0 0

相关博文

我的热门文章

img
取 消
img