资源文件中的DLL,在内存中如何调用 小雨哥,还有点问题(100)

H

hying95

Unregistered / Unconfirmed
GUEST, unregistred user!
资源文件中的DLL,在内存中如何调用这个是在网上找的{内存加载DLL}unit PELoader;interfaceuses Windows;function LoadPE(Buf: Pointer; Len: Integer): Cardinal;procedure FreePE(Handle: Cardinal);function My_GetProcAddress(Module: Cardinal; ProcessName: PChar): Pointer;implementationconst IMAGE_ORDINAL_FLAG = DWORD($80000000);function My_GetProcAddress(Module: Cardinal; ProcessName: PChar): Pointer; function strcmp(p1, p2: PChar): boolean; begin Result := False; while (p1^ = p2^) do begin if (P1^ = #0) or (P2^ = #0) then begin Result := True; Exit; end; Inc(P1); Inc(P2); end; end;var ExportName : pChar; Address : Cardinal; J : Cardinal; ImageDosHeader : PImageDosHeader; ImageNTHeaders : PImageNTHeaders; ImageExportDirectory: PImageExportDirectory;begin ImageDosHeader := Pointer(Module); ImageNTHeaders := Pointer(Module + ImageDosHeader._lfanew); ImageExportDirectory := Pointer(ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + Module); J := 0; Address := 0; repeat ExportName := Pointer(Cardinal(Pointer(Cardinal(ImageExportDirectory.AddressOfNames) + Module + J * 4)^) + Module); if strcmp(ExportName, ProcessName) then Address := Cardinal(Pointer(Word(Pointer(J shl 1 + Cardinal( ImageExportDirectory.AddressOfNameOrdinals) + Module)^) and $0000FFFF shl 2 + Cardinal(ImageExportDirectory.AddressOfFunctions) + Module)^) + Module; Inc(J); until (Address <> 0) or (J = ImageExportDirectory.NumberOfNames); Result := Pointer(Address);end; type TImageSectionHeaders = array[0..0] of TImageSectionHeader; PImageSectionHeaders = ^TImageSectionHeaders; TIIDUnion = record case Integer of 0: (Characteristics: DWORD); 1: (OriginalFirstThunk: DWORD); end; PIMAGE_IMPORT_DESCRIPTOR = ^IMAGE_IMPORT_DESCRIPTOR; _IMAGE_IMPORT_DESCRIPTOR = record Union: TIIDUnion; TimeDateStamp: DWORD; ForwarderChain: DWORD; Name: DWORD; FirstThunk: DWORD; end; IMAGE_IMPORT_DESCRIPTOR = _IMAGE_IMPORT_DESCRIPTOR; TImageImportDecriptor = IMAGE_IMPORT_DESCRIPTOR; PImageImportDecriptor = PIMAGE_IMPORT_DESCRIPTOR; PIMAGE_THUNK_DATA32 = ^IMAGE_THUNK_DATA32; _IMAGE_THUNK_DATA32 = record case Integer of 0: (ForwarderString: DWORD); 1: (Function_: DWORD); 2: (Ordinal: DWORD); 3: (AddressOfData: DWORD); end; IMAGE_THUNK_DATA32 = _IMAGE_THUNK_DATA32; TImageThunkData32 = IMAGE_THUNK_DATA32; PImageThunkData32 = PIMAGE_THUNK_DATA32; IMAGE_THUNK_DATA = IMAGE_THUNK_DATA32; PIMAGE_THUNK_DATA = PIMAGE_THUNK_DATA32; PIMAGE_IMPORT_BY_NAME = ^IMAGE_IMPORT_BY_NAME; _IMAGE_IMPORT_BY_NAME = record Hint: Word; Name: array[0..0] of Byte; end; IMAGE_IMPORT_BY_NAME = _IMAGE_IMPORT_BY_NAME; TImageImportByName = IMAGE_IMPORT_BY_NAME; PImageImportByName = PIMAGE_IMPORT_BY_NAME;{ 计算对齐后的大小 }function GetAlignedSize(Origin, Alignment: Cardinal): Cardinal;begin result := (Origin + Alignment - 1) div Alignment * Alignment;end;{ 计算加载pe并对齐需要占用多少内存,未直接使用OptionalHeader.SizeOfImage作为结果是因为据说有的编译器生成的exe这个值会填0 }function CalcTotalImageSize(MzH: PImageDosHeader; FileLen: Cardinal; peH: PImageNtHeaders; peSecH: PImageSectionHeaders): Cardinal;var i : Integer;begin {计算pe头的大小} result := GetAlignedSize(PeH.OptionalHeader.SizeOfHeaders, PeH.OptionalHeader.SectionAlignment); {计算所有节的大小} for i := 0 to peH.FileHeader.NumberOfSections - 1 do if peSecH.PointerToRawData + peSecH.SizeOfRawData > FileLen then // 超出文件范围 begin result := 0; exit; end else if peSecH.VirtualAddress <> 0 then //计算对齐后某节的大小 if peSecH.Misc.VirtualSize <> 0 then result := GetAlignedSize(peSecH.VirtualAddress + peSecH.Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment) else result := GetAlignedSize(peSecH.VirtualAddress + peSecH.SizeOfRawData, PeH.OptionalHeader.SectionAlignment) else if peSecH.Misc.VirtualSize < peSecH.SizeOfRawData then result := result + GetAlignedSize(peSecH.SizeOfRawData, peH.OptionalHeader.SectionAlignment) else result := result + GetAlignedSize(peSecH.Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment);end;{ 加载pe到内存并对齐所有节 }function AlignPEToMem(const Buf: Pointer; Len: Integer; var PeH: PImageNtHeaders; var PeSecH: PImageSectionHeaders; var Mem: Pointer; var ImageSize: Cardinal): Boolean;var SrcMz : PImageDosHeader; // DOS头 SrcPeH : PImageNtHeaders; // PE头 SrcPeSecH : PImageSectionHeaders; // 节表 i : Integer; l : Cardinal; Pt : Pointer;begin result := false; SrcMz := Buf; if Len < sizeof(TImageDosHeader) then exit; if SrcMz.e_magic <> IMAGE_DOS_SIGNATURE then exit; if Len < SrcMz._lfanew + Sizeof(TImageNtHeaders) then exit; SrcPeH := pointer(Integer(SrcMz) + SrcMz._lfanew); if (SrcPeH.Signature <> IMAGE_NT_SIGNATURE) then exit; if (SrcPeH.FileHeader.Characteristics and IMAGE_FILE_DLL = 0) //不是DLL, or (SrcPeH.FileHeader.Characteristics and IMAGE_FILE_EXECUTABLE_IMAGE = 0) //不可执行 or (SrcPeH.FileHeader.SizeOfOptionalHeader <> SizeOf(TImageOptionalHeader)) then exit; SrcPeSecH := Pointer(Integer(SrcPeH) + SizeOf(TImageNtHeaders)); ImageSize := CalcTotalImageSize(SrcMz, Len, SrcPeH, SrcPeSecH); if ImageSize = 0 then exit; Mem := VirtualAlloc(nil, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 分配一块可以执行,可以读写的内存 if Mem <> nil then begin // 计算需要复制的PE头 l := SrcPeH.OptionalHeader.SizeOfHeaders; for i := 0 to SrcPeH.FileHeader.NumberOfSections - 1 do if (SrcPeSecH.PointerToRawData <> 0) and (SrcPeSecH.PointerToRawData < l) then l := SrcPeSecH.PointerToRawData; Move(SrcMz^, Mem^, l); PeH := Pointer(Integer(Mem) + PImageDosHeader(Mem)._lfanew); PeSecH := Pointer(Integer(PeH) + sizeof(TImageNtHeaders)); Pt := Pointer(Cardinal(Mem) + GetAlignedSize(PeH.OptionalHeader.SizeOfHeaders, PeH.OptionalHeader.SectionAlignment)); for i := 0 to PeH.FileHeader.NumberOfSections - 1 do begin // 定位该节在内存中的位置 if PeSecH.VirtualAddress <> 0 then Pt := Pointer(Cardinal(Mem) + PeSecH.VirtualAddress); if PeSecH.SizeOfRawData <> 0 then begin // 复制数据到内存 Move(Pointer(Cardinal(SrcMz) + PeSecH.PointerToRawData)^, pt^, PeSecH.SizeOfRawData); if peSecH.Misc.VirtualSize < peSecH.SizeOfRawData then pt := pointer(Cardinal(pt) + GetAlignedSize(PeSecH.SizeOfRawData, PeH.OptionalHeader.SectionAlignment)) else pt := pointer(Cardinal(pt) + GetAlignedSize(peSecH.Misc.VirtualSize, peH.OptionalHeader.SectionAlignment)); // pt 定位到下一节开始位置 end else pt := pointer(Cardinal(pt) + GetAlignedSize(PeSecH.Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment)); end; result := True; end;end;{ 是否包含可重定向列表 }function HasRelocationTable(peH: PImageNtHeaders): Boolean;begin result := (peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> 0) and (peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size <> 0);end;type PImageBaseRelocation = ^TImageBaseRelocation; TImageBaseRelocation = packed record VirtualAddress: cardinal; SizeOfBlock: cardinal; end;{ 重定向PE用到的地址 }procedure DoRelocation(peH: PImageNtHeaders; NewBase: Pointer);var Delta : Cardinal; p : PImageBaseRelocation; pw : PWord; i : Integer;begin Delta := Cardinal(NewBase) - peH.OptionalHeader.ImageBase; p := pointer(cardinal(NewBase) + peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); while (p.VirtualAddress + p.SizeOfBlock <> 0) do begin pw := pointer(Integer(p) + Sizeof(TImageBaseRelocation)); for i := 1 to (p.SizeOfBlock - Sizeof(TImageBaseRelocation)) div sizeof(WORD) do begin if pw^ and $F000 = $3000 then Inc(PCardinal(Cardinal(NewBase) + p.VirtualAddress + (pw^ and $0FFF))^, Delta); inc(pw); end; p := PImageBaseRelocation(pw); end;end;{填充引入地址表}function FillImports(peH: PImageNtHeaders; pImageBase: Pointer): BOOL;type TIMAGE_THUNK_DATAs = array[0..0] of IMAGE_THUNK_DATA; PIMAGE_THUNK_DATAs = ^TIMAGE_THUNK_DATAs;var Offset : Cardinal; pID : PIMAGE_IMPORT_DESCRIPTOR; pRealIAT, pOriginalIAT: PIMAGE_THUNK_DATAs; buf : array[0..$FF] of char; pName : PChar; I : Integer; hDll : HMODULE; lpFunction : Pointer; pByName : PIMAGE_IMPORT_BY_NAME;begin Result := True; Offset := peH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; if Offset = 0 then //无导入表 Exit; pID := PIMAGE_IMPORT_DESCRIPTOR(Cardinal(pImageBase) + Offset); while (pID^.Union.Characteristics <> 0) do begin pRealIAT := PIMAGE_THUNK_DATAs(Cardinal(pImageBase) + pID^.FirstThunk); pOriginalIAT := PIMAGE_THUNK_DATAs(Cardinal(pImageBase) + pID^.Union.OriginalFirstThunk); //获取DLL名字 PName := PChar(Cardinal(pImageBase) + pID^.Name); FillMemory(@Buf, $FF, 0); for i := 0 to $FF do begin if (pName = #0) then break; buf := pName; end; //判断是否加载过,没加载过就加载 hDLL := GetModuleHandle(Buf); if hDLL = 0 then hDLL := LoadLibrary(Buf); I := 0; while True do begin if (pOriginalIAT.Function_ = 0) then break; lpFunction := nil; if (pOriginalIAT.Ordinal and IMAGE_ORDINAL_FLAG <> 0) then //按序号 begin lpFunction := Windows.GetProcAddress(hDll, PChar(pOriginalIAT.Ordinal and $0000FFFF)); end else //按名字 begin //获取此IAT项所描述的函数名称 pByName := PIMAGE_IMPORT_BY_NAME (DWORD(pImageBase) + DWORD(pOriginalIAT.AddressOfData)); lpFunction := Windows.GetProcAddress(hDll, PChar(@pByName^.Name)); if (lpFunction <> nil) then //找到了! pRealIAT.Function_ := DWORD(lpFunction) else begin Result := False; Exit; end; end; Inc(I); end; pID := PIMAGE_IMPORT_DESCRIPTOR(DWORD(pID) + sizeof(IMAGE_IMPORT_DESCRIPTOR)); end;end;function LoadPE(Buf: Pointer; Len: Integer): Cardinal;var peH : PImageNtHeaders; //PE头 peSecH : PImageSectionHeaders; peSz : Cardinal; P : Pointer; DLLMain : function(hinstDLL: Cardinal; fdwReason, lpvReserved: DWORD): BOOL; stdcall;begin //分配可执行的内存块 if alignPEToMem(Buf, Len, peH, peSecH, P, peSz) then begin if HasRelocationTable(peH) then //如果有重定位表就进行重定位 DoRelocation(peH, P); FillImports(peH, P); //填写导入表 //获取并执行动态链接库的入口函数 DLLMain := Pointer(peH^.OptionalHeader.AddressOfEntryPoint + DWORD(P)); DLLMain(DWORD(P), DLL_PROCESS_ATTACH, 0); Result := Cardinal(P); end else Result := 0;end;procedure FreePE(Handle: Cardinal);var dosH : PImageDosHeader; peH : PImageNtHeaders; //PE头 DLLMain : function(hinstDLL: Cardinal; fdwReason, lpvReserved: DWORD): BOOL; stdcall; P : Pointer;begin P := Pointer(Handle); dosH := PImageDosHeader(P); peH := PImageNtHeaders(DWORD(P)+dosH^._lfanew); DLLMain := Pointer(peH^.OptionalHeader.AddressOfEntryPoint + DWORD(P)); DLLMain(DWORD(P), DLL_PROCESS_DETACH, 0); //反初始化DLL VirtualFreeEx(GetCurrentProcess(), P, 0, MEM_RELEASE);end;end.//**********************************************************************作者给出的调用方式如下,我调用不成功。请大家帮忙调用var hDLL : Cardinal; A:Cardinal;begin hDLL := LoadPE('DLL的数据,DLL数据的大小'); A := PELoader.GetProcAddress(DWORD(hDLL), 'A'); A('aa'); FreePE(hDLL);end;
 
P

pascal

Unregistered / Unconfirmed
GUEST, unregistred user!
hres := findresource(nil, "DllName", TypeName);if hres <> nil then res = LoadResource(0, hRes);Ptr = LockResource(Data); LoadPE(ptr, sizeofres);
 
H

hying95

Unregistered / Unconfirmed
GUEST, unregistred user!
library CSdll;uses //FastMM4, Windows;{$R *.res}var vMsg : String = 'abc';//用一个全局变量可以检查重定位是否正确.重定位不正确一定访问不到这个变量.procedure Pro(Msg : PChar);begin MessageBox(0, PChar(vMsg + Msg),'', MB_OK);end;exports Pro;beginend.
 
H

hying95

Unregistered / Unconfirmed
GUEST, unregistred user!
Men_PE.rcPE ExeFile CSdll.dll
 
H

hying95

Unregistered / Unconfirmed
GUEST, unregistred user!
procedure TForm1.Button1Click(Sender: TObject);var A: procedure (Msg : PChar);stdcall; hres: HRSRC; res: Cardinal; ptr: Pointer; hDll : Cardinal;begin hres:=findresource(0,'CSdll.dll','ExeFile'); if hres<>0then res:=LoadResource(0,hres); ptr:=LockResource(0); hDLL := LoadPE(ptr,sizeof(ptr)); A := PELoader.GetProcAddress(DWORD(hDLL), 'A'); A('aa'); FreePE(hDLL);end;编译后点BUTTON1出错
 
H

hying95

Unregistered / Unconfirmed
GUEST, unregistred user!
to pascal!恳请继续指导
 
H

hying95

Unregistered / Unconfirmed
GUEST, unregistred user!
怎么没人回答呢
 

白河愁

Unregistered / Unconfirmed
GUEST, unregistred user!
重定义位置处理有 Bug
 
H

hying95

Unregistered / Unconfirmed
GUEST, unregistred user!
这个是在网上找的,自己不会搞
 
H

husion

Unregistered / Unconfirmed
GUEST, unregistred user!
记得DLL中只能用么字符,不能用字符串
 

白河愁

Unregistered / Unconfirmed
GUEST, unregistred user!
网上的代码就是这样。想好的话自己改写或者花钱买吧
 
H

hying95

Unregistered / Unconfirmed
GUEST, unregistred user!
白河愁大侠重定义位置处理有 Bug,能否处理下?
 
H

hying95

Unregistered / Unconfirmed
GUEST, unregistred user!
不知道wr960204能否看到
 

小雨哥

Unregistered / Unconfirmed
GUEST, unregistred user!
第一步 - 建立包含dll的res资源文件:1)新建一个 rc 后缀的文本文件,例如:csdll.rc2)用记事本打开 csdll.rc 文件,在里面写入一行如下文字:CSDLL CDATA "csdll.dll"3)将 csdll.dll 这个文件放入到这里,保证在进行资源编译时,能够找到这个 dll4)新建一个dos批文件在这个目录(假设你愿意把这个批文件命名为rc.bat),里面写好资源编译命令并保存,如下:brcc32 csdll.rc5)双击执行这个rc.bat,文件夹里会生成一个 csdll.res 文件,这个文件就是要放入exe里面的资源文件。第二步 - 将含有dll的资源包含到程序里:1)将刚刚编译好的 csdll.res 放到你exe程序文件夹里。2)在你源代码的某个地方把这个资源文件加上去,如下:{$R CSDLL.RES}3)这时候如果再编译你的exe项目,程序会把新放入的res文件一起编入exe,所以可以看到编译后的程序变大了。第三步 - 调用dll的内存加载单元里的函数使用这个dll:1)写一个函数把这个res资源里的dll加载到你的程序里。2)写一个函数把这个dll里的函数取出来。3)使用完成后清除这个内存里的dll。第三步完整的代码示例贴出在下面,其中包含了文字说明,仅供参考:--- 第三步代码的完整参考示例 ---/// 全局的资源DLL模块变量var g_dll_module:Cardinal;/// 1)这个函数把res资源里的dll加载到程序里function LoadDLLFromResource(const ResourceName: String): Cardinal;var ms: TMemoryStream; rs: TResourceStream;begin ms := TMemoryStream.Create; try rs := TResourceStream.Create(HInstance, ResourceName, 'CDATA'); try ms.CopyFrom(rs, 0); ms.Position := 0; finally rs.Free; end; Result := LoadPE(ms.Memory, ms.Size); finally ms.Free; end;end;/// 2)这个函数把这个dll里的函数取出来function GetAddrFromMemoryDll(dll_module:Cardinal; const Name: string):pointer;begin try Result := XXX_GetProcAddress(dll_module, PAnsiChar(Name)); /// 这个函数最好改个名,容易与 Windows 的同名函数混淆,所以我特地前面加了三个XXX_ except Result := nil; end;end;--- 代码总结 ---在程序的某个地方先初始化这个g_dll_module变量:g_dll_module := LoadDLLFromResource('CSDLL'); /// 注意这里的字符串就是刚才资源里的名字!之后,就可以取到dll里函数的地址了,如下(假设里面有函数 A 和 函数 B,并且已经对函数A、B做了适当的定义,定义方法参考Delphi下动态加载 DLL 里函数的方法);@A := GetAddrFromMemoryDll(g_dll_module, 'A'); /// 取得函数 A@B := GetAddrFromMemoryDll(g_dll_module, 'B'); /// 取得函数 B取到函数A和B以后,就可以使用这二个函数了。当程序将要结束,可以调用FreePE函数清除内存如下:FreePE(g_dll_module);
 

小雨哥

Unregistered / Unconfirmed
GUEST, unregistred user!
注意上面那个 XXX_GetProcAddress 是你贴出的单元里的那个,不是 Windows 里的。这个函数名字取名不好,建议将名字改成 GetProcAddressPE 后使用,否则容易与 Windows API 里的同名函数冲突。我在发这个贴时,就3次修改了上面那个那个帖子,这样取名要害死人的。另外,建议你主贴的标题改一改,最好改成“从内存中加载 DLL”的名字,这样可以方便以后的人参考。
 
H

hying95

Unregistered / Unconfirmed
GUEST, unregistred user!
多谢小雨哥,明天试下,不懂再问你以前是“从内存加载 DLL”很久了,没有人回答 到时我改回来
 
H

hying95

Unregistered / Unconfirmed
GUEST, unregistred user!
小雨哥运行出错 GetProcAddress已经改成 My_GetProcAddressaccess violation at 0x0000624e: read of address 0x0000624e.进程运行终止unit UDll;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, PELoader;type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end;var Form1: TForm1; /// 全局的资源DLL模块变量 g_dll_module:Cardinal;implementation{$R *.dfm}{$R CSdll.res}/// 1)这个函数把res资源里的dll加载到程序里function LoadDLLFromResource(const ResourceName: String): Cardinal;var ms: TMemoryStream; rs: TResourceStream;begin ms := TMemoryStream.Create; try rs := TResourceStream.Create(HInstance, ResourceName, 'CDATA'); try ms.CopyFrom(rs, 0); ms.Position := 0; finally rs.Free; end; Result := LoadPE(ms.Memory, ms.Size); finally ms.Free; end;end;/// 2)这个函数把这个dll里的函数取出来function GetAddrFromMemoryDll(dll_module:Cardinal; const Name: string):pointer;begin try Result := My_GetProcAddress(dll_module, PAnsiChar(Name)); /// 这个函数最好改个名,容易与 Windows 的同名函数混淆,所以我特地前面加了三个XXX_ except Result := nil; end;end;procedure TForm1.Button1Click(Sender: TObject);begin g_dll_module := LoadDLLFromResource('CSDLL');end;end.
 
H

hying95

Unregistered / Unconfirmed
GUEST, unregistred user!
//完整代码 已经贴到1楼
 

小雨哥

Unregistered / Unconfirmed
GUEST, unregistred user!
嗯。我忘了说了,Delphi 2010 等版本是支持Unicode字符集的版本,这样的版本不能直接调用以上代码。可以做些改变,以下是改变后的 PELoader.pas ,实际上也没有改,就是将单元里的字符串强制定义成 Ansi 字符集。
 

小雨哥

Unregistered / Unconfirmed
GUEST, unregistred user!
强制了字符集的版本,可以通用在 Unicode 版本和普通版本的 Delphi 下:unit PELoader;interfaceuses Windows;function LoadPE(Buf: Pointer; Len: Integer): Cardinal;procedure FreePE(Handle: Cardinal);function GetProcAddressPE(Module: Cardinal; ProcessName: PAnsiChar): Pointer;implementationconst IMAGE_ORDINAL_FLAG = DWORD($80000000);function GetProcAddressPE(Module: Cardinal; ProcessName: PAnsiChar): Pointer; function strcmp(p1, p2: PAnsiChar): boolean; begin Result := False; while (p1^ = p2^) do begin if (P1^ = #0) or (P2^ = #0) then begin Result := True; Exit; end; Inc(P1); Inc(P2); end; end;var ExportName : PAnsiChar; Address : Cardinal; J : Cardinal; ImageDosHeader : PImageDosHeader; ImageNTHeaders : PImageNTHeaders; ImageExportDirectory: PImageExportDirectory;begin ImageDosHeader := Pointer(Module); ImageNTHeaders := Pointer(Module + ImageDosHeader._lfanew); ImageExportDirectory := Pointer(ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + Module); J := 0; Address := 0; repeat ExportName := Pointer(Cardinal(Pointer(Cardinal(ImageExportDirectory.AddressOfNames) + Module + J * 4)^) + Module); if strcmp(ExportName, ProcessName) then Address := Cardinal(Pointer(Word(Pointer(J shl 1 + Cardinal( ImageExportDirectory.AddressOfNameOrdinals) + Module)^) and $0000FFFF shl 2 + Cardinal(ImageExportDirectory.AddressOfFunctions) + Module)^) + Module; Inc(J); until (Address <> 0) or (J = ImageExportDirectory.NumberOfNames); Result := Pointer(Address);end; type TImageSectionHeaders = array[0..0] of TImageSectionHeader; PImageSectionHeaders = ^TImageSectionHeaders; TIIDUnion = record case Integer of 0: (Characteristics: DWORD); 1: (OriginalFirstThunk: DWORD); end; PIMAGE_IMPORT_DESCRIPTOR = ^IMAGE_IMPORT_DESCRIPTOR; _IMAGE_IMPORT_DESCRIPTOR = record Union: TIIDUnion; TimeDateStamp: DWORD; ForwarderChain: DWORD; Name: DWORD; FirstThunk: DWORD; end; IMAGE_IMPORT_DESCRIPTOR = _IMAGE_IMPORT_DESCRIPTOR; TImageImportDecriptor = IMAGE_IMPORT_DESCRIPTOR; PImageImportDecriptor = PIMAGE_IMPORT_DESCRIPTOR; PIMAGE_THUNK_DATA32 = ^IMAGE_THUNK_DATA32; _IMAGE_THUNK_DATA32 = record case Integer of 0: (ForwarderString: DWORD); 1: (Function_: DWORD); 2: (Ordinal: DWORD); 3: (AddressOfData: DWORD); end; IMAGE_THUNK_DATA32 = _IMAGE_THUNK_DATA32; TImageThunkData32 = IMAGE_THUNK_DATA32; PImageThunkData32 = PIMAGE_THUNK_DATA32; IMAGE_THUNK_DATA = IMAGE_THUNK_DATA32; PIMAGE_THUNK_DATA = PIMAGE_THUNK_DATA32; PIMAGE_IMPORT_BY_NAME = ^IMAGE_IMPORT_BY_NAME; _IMAGE_IMPORT_BY_NAME = record Hint: Word; Name: array[0..0] of Byte; end; IMAGE_IMPORT_BY_NAME = _IMAGE_IMPORT_BY_NAME; TImageImportByName = IMAGE_IMPORT_BY_NAME; PImageImportByName = PIMAGE_IMPORT_BY_NAME;{ 计算对齐后的大小 }function GetAlignedSize(Origin, Alignment: Cardinal): Cardinal;begin result := (Origin + Alignment - 1) div Alignment * Alignment;end;{ 计算加载pe并对齐需要占用多少内存,未直接使用OptionalHeader.SizeOfImage作为结果是因为据说有的编译器生成的exe这个值会填0 }function CalcTotalImageSize(MzH: PImageDosHeader; FileLen: Cardinal; peH: PImageNtHeaders; peSecH: PImageSectionHeaders): Cardinal;var i : Integer;begin {计算pe头的大小} result := GetAlignedSize(PeH.OptionalHeader.SizeOfHeaders, PeH.OptionalHeader.SectionAlignment); {计算所有节的大小} for i := 0 to peH.FileHeader.NumberOfSections - 1 do if peSecH.PointerToRawData + peSecH.SizeOfRawData > FileLen then // 超出文件范围 begin result := 0; exit; end else if peSecH.VirtualAddress <> 0 then //计算对齐后某节的大小 if peSecH.Misc.VirtualSize <> 0 then result := GetAlignedSize(peSecH.VirtualAddress + peSecH.Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment) else result := GetAlignedSize(peSecH.VirtualAddress + peSecH.SizeOfRawData, PeH.OptionalHeader.SectionAlignment) else if peSecH.Misc.VirtualSize < peSecH.SizeOfRawData then result := result + GetAlignedSize(peSecH.SizeOfRawData, peH.OptionalHeader.SectionAlignment) else result := result + GetAlignedSize(peSecH.Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment);end;{ 加载pe到内存并对齐所有节 }function AlignPEToMem(const Buf: Pointer; Len: Integer; var PeH: PImageNtHeaders; var PeSecH: PImageSectionHeaders; var Mem: Pointer; var ImageSize: Cardinal): Boolean;var SrcMz : PImageDosHeader; // DOS头 SrcPeH : PImageNtHeaders; // PE头 SrcPeSecH : PImageSectionHeaders; // 节表 i : Integer; l : Cardinal; Pt : Pointer;begin result := false; SrcMz := Buf; if Len < sizeof(TImageDosHeader) then exit; if SrcMz.e_magic <> IMAGE_DOS_SIGNATURE then exit; if Len < SrcMz._lfanew + Sizeof(TImageNtHeaders) then exit; SrcPeH := pointer(Integer(SrcMz) + SrcMz._lfanew); if (SrcPeH.Signature <> IMAGE_NT_SIGNATURE) then exit; if (SrcPeH.FileHeader.Characteristics and IMAGE_FILE_DLL = 0) //不是DLL, or (SrcPeH.FileHeader.Characteristics and IMAGE_FILE_EXECUTABLE_IMAGE = 0) //不可执行 or (SrcPeH.FileHeader.SizeOfOptionalHeader <> SizeOf(TImageOptionalHeader)) then exit; SrcPeSecH := Pointer(Integer(SrcPeH) + SizeOf(TImageNtHeaders)); ImageSize := CalcTotalImageSize(SrcMz, Len, SrcPeH, SrcPeSecH); if ImageSize = 0 then exit; Mem := VirtualAlloc(nil, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 分配一块可以执行,可以读写的内存 if Mem <> nil then begin // 计算需要复制的PE头 l := SrcPeH.OptionalHeader.SizeOfHeaders; for i := 0 to SrcPeH.FileHeader.NumberOfSections - 1 do if (SrcPeSecH.PointerToRawData <> 0) and (SrcPeSecH.PointerToRawData < l) then l := SrcPeSecH.PointerToRawData; Move(SrcMz^, Mem^, l); PeH := Pointer(Integer(Mem) + PImageDosHeader(Mem)._lfanew); PeSecH := Pointer(Integer(PeH) + sizeof(TImageNtHeaders)); Pt := Pointer(Cardinal(Mem) + GetAlignedSize(PeH.OptionalHeader.SizeOfHeaders, PeH.OptionalHeader.SectionAlignment)); for i := 0 to PeH.FileHeader.NumberOfSections - 1 do begin // 定位该节在内存中的位置 if PeSecH.VirtualAddress <> 0 then Pt := Pointer(Cardinal(Mem) + PeSecH.VirtualAddress); if PeSecH.SizeOfRawData <> 0 then begin // 复制数据到内存 Move(Pointer(Cardinal(SrcMz) + PeSecH.PointerToRawData)^, pt^, PeSecH.SizeOfRawData); if peSecH.Misc.VirtualSize < peSecH.SizeOfRawData then pt := pointer(Cardinal(pt) + GetAlignedSize(PeSecH.SizeOfRawData, PeH.OptionalHeader.SectionAlignment)) else pt := pointer(Cardinal(pt) + GetAlignedSize(peSecH.Misc.VirtualSize, peH.OptionalHeader.SectionAlignment)); // pt 定位到下一节开始位置 end else pt := pointer(Cardinal(pt) + GetAlignedSize(PeSecH.Misc.VirtualSize, PeH.OptionalHeader.SectionAlignment)); end; result := True; end;end;{ 是否包含可重定向列表 }function HasRelocationTable(peH: PImageNtHeaders): Boolean;begin result := (peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> 0) and (peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size <> 0);end;type PImageBaseRelocation = ^TImageBaseRelocation; TImageBaseRelocation = packed record VirtualAddress: cardinal; SizeOfBlock: cardinal; end;{ 重定向PE用到的地址 }procedure DoRelocation(peH: PImageNtHeaders; NewBase: Pointer);var Delta : Cardinal; p : PImageBaseRelocation; pw : PWord; i : Integer;begin Delta := Cardinal(NewBase) - peH.OptionalHeader.ImageBase; p := pointer(cardinal(NewBase) + peH.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); while (p.VirtualAddress + p.SizeOfBlock <> 0) do begin pw := pointer(Integer(p) + Sizeof(TImageBaseRelocation)); for i := 1 to (p.SizeOfBlock - Sizeof(TImageBaseRelocation)) div sizeof(WORD) do begin if pw^ and $F000 = $3000 then Inc(PCardinal(Cardinal(NewBase) + p.VirtualAddress + (pw^ and $0FFF))^, Delta); inc(pw); end; p := PImageBaseRelocation(pw); end;end;{填充引入地址表}function FillImports(peH: PImageNtHeaders; pImageBase: Pointer): BOOL;type TIMAGE_THUNK_DATAs = array[0..0] of IMAGE_THUNK_DATA; PIMAGE_THUNK_DATAs = ^TIMAGE_THUNK_DATAs;var Offset : Cardinal; pID : PIMAGE_IMPORT_DESCRIPTOR; pRealIAT, pOriginalIAT: PIMAGE_THUNK_DATAs; buf : array[0..$FF] of AnsiChar; pName : PAnsiChar; I : Integer; hDll : HMODULE; lpFunction : Pointer; pByName : PIMAGE_IMPORT_BY_NAME;begin Result := True; Offset := peH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; if Offset = 0 then //无导入表 Exit; pID := PIMAGE_IMPORT_DESCRIPTOR(Cardinal(pImageBase) + Offset); while (pID^.Union.Characteristics <> 0) do begin pRealIAT := PIMAGE_THUNK_DATAs(Cardinal(pImageBase) + pID^.FirstThunk); pOriginalIAT := PIMAGE_THUNK_DATAs(Cardinal(pImageBase) + pID^.Union.OriginalFirstThunk); //获取DLL名字 PName := PAnsiChar(Cardinal(pImageBase) + pID^.Name); FillMemory(@Buf, $FF, 0); for i := 0 to $FF do begin if (pName = #0) then break; buf := pName; end; //判断是否加载过,没加载过就加载 hDLL := GetModuleHandleA(Buf); if hDLL = 0 then hDLL := LoadLibraryA(Buf); I := 0; while True do begin if (pOriginalIAT.Function_ = 0) then break; lpFunction := nil; if (pOriginalIAT.Ordinal and IMAGE_ORDINAL_FLAG <> 0) then //按序号 begin lpFunction := Windows.GetProcAddress(hDll, PAnsiChar(pOriginalIAT.Ordinal and $0000FFFF)); end else //按名字 begin //获取此IAT项所描述的函数名称 pByName := PIMAGE_IMPORT_BY_NAME (DWORD(pImageBase) + DWORD(pOriginalIAT.AddressOfData)); lpFunction := Windows.GetProcAddress(hDll, PAnsiChar(@pByName^.Name)); if (lpFunction <> nil) then //找到了! pRealIAT.Function_ := DWORD(lpFunction) else begin Result := False; Exit; end; end; Inc(I); end; pID := PIMAGE_IMPORT_DESCRIPTOR(DWORD(pID) + sizeof(IMAGE_IMPORT_DESCRIPTOR)); end;end;function LoadPE(Buf: Pointer; Len: Integer): Cardinal;var peH : PImageNtHeaders; //PE头 peSecH : PImageSectionHeaders; peSz : Cardinal; P : Pointer; DLLMain : function(hinstDLL: Cardinal; fdwReason, lpvReserved: DWORD): BOOL; stdcall;begin //分配可执行的内存块 if alignPEToMem(Buf, Len, peH, peSecH, P, peSz) then begin if HasRelocationTable(peH) then //如果有重定位表就进行重定位 DoRelocation(peH, P); FillImports(peH, P); //填写导入表 //获取并执行动态链接库的入口函数 DLLMain := Pointer(peH^.OptionalHeader.AddressOfEntryPoint + DWORD(P)); DLLMain(DWORD(P), DLL_PROCESS_ATTACH, 0); Result := Cardinal(P); end else Result := 0;end;procedure FreePE(Handle: Cardinal);var dosH : PImageDosHeader; peH : PImageNtHeaders; //PE头 DLLMain : function(hinstDLL: Cardinal; fdwReason, lpvReserved: DWORD): BOOL; stdcall; P : Pointer;begin P := Pointer(Handle); dosH := PImageDosHeader(P); peH := PImageNtHeaders(DWORD(P)+dosH^._lfanew); DLLMain := Pointer(peH^.OptionalHeader.AddressOfEntryPoint + DWORD(P)); DLLMain(DWORD(P), DLL_PROCESS_DETACH, 0); //反初始化DLL VirtualFreeEx(GetCurrentProcess(), P, 0, MEM_RELEASE);end;end.
 
顶部