//去年写的一份内存加载,某人用的还不错,扔出来给有需要的人,无坑。 unit MemoryLoadLibraryUnit; interface uses Winapi.Windows,System.SysUtils; function LoadLibraryX(const FileMemory:Pointer;const FileSize:ULONG):THandle;overload; function LoadLibraryX(LibraryName:PAnsiChar):THandle;overload; function GetMemoryLibraryProcAddress(Const MemoryLibrary:THandle;const API:PAnsiChar):Pointer; implementation function LoadLibraryX(LibraryName:PAnsiChar):THandle; var fileHandle:THandle; Mem:Pointer; Size,bytesize:ULONG32; begin Result := 0; fileHandle := CreateFileA(LibraryName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); if fileHandle <> INVALID_HANDLE_VALUE then begin Size := GetFileSize(fileHandle,nil); Mem := AllocMem(Size); if ReadFile(fileHandle,MEM^,Size,bytesize,NIL) then begin Result := LoadLibraryX(Mem,Size); end; CloseHandle(fileHandle); FreeMem(Mem,Size); end; end; //兼容32&64的内存加载模块 作者:妖蛋 //参考:各种各种 //特别崇拜一下武大神 function LoadLibraryX(const FileMemory:Pointer;const FileSize:ULONG):THandle; type _IMAGE_BASE_RELOCATION = packed record VirtualAddress:dword; //重定位数据开始RVA SizeOfBlock:dword; //重定位长度 (SizeOfBlock - $8)/2 end; PIMAGE_BASE_RELOCATION = ^_IMAGE_BASE_RELOCATION; PImageBaseRelocation = PIMAGE_BASE_RELOCATION; PRUNTIME_FUNCTION = ^TRUNTIME_FUNCTION; TRUNTIME_FUNCTION = record FunctionStart: LongWord; FunctionEnd: LongWord; UnwindInfo: LongWord; end; var NtHeaders:PImageNtHeaders; ImageBase:NativeUInt; LoadMem:Pointer; IsImageBaseLoad:Boolean; SectionArray:array of TImageSectionHeader; protectvalue, oldProtect: DWORD; pgsize: NativeUInt; protect:DWORD; i:DWORD; ImportHeader:PImageImportDescriptor; ImportDllHandle:THandle; ThunkP,WriteThunkP:PImageThunkData; BaseRelocation:PImageBaseRelocation; BaseItem:Pointer; BaseNumber:NativeUInt; P:Pointer; PruntIME: PRUNTIME_FUNCTION; PruntIMENumber:DWORD; DllMain: function (dwHandle:THandle;dwReason:DWORD;dwReserved: Pointer): Boolean; stdcall; RtlAddFunctionTable:function (FunctionTable:PRUNTIME_FUNCTION;EntryCount:DWORD;BaseAddress:DWORD64):Boolean;cdecl; label OVER; begin Result := 0; //获取PE头信息 NtHeaders := PImageNtHeaders(Pointer(NativeUInt(FileMemory) + PImageDosHeader(FileMemory)^._lfanew)); //获取加载地址 ImageBase := NtHeaders.OptionalHeader.ImageBase; //尝试加载 LoadMem := AllocMem(NtHeaders.OptionalHeader.SizeOfImage); //将页面调整为可读可写可执行,所有都完成后再修正内存页属性 VirtualProtect(LoadMem,NtHeaders.OptionalHeader.SizeOfImage,PAGE_EXECUTE_READWRITE,&protect); //判断是否为默认加载位置 if NativeUInt(LoadMem) = ImageBase then IsImageBaseLoad := True else IsImageBaseLoad := False; //复制PE头到加载地址 Move(FileMemory^,LoadMem^,NtHeaders.OptionalHeader.SizeOfHeaders); //申请区块数组 SetLength(SectionArray,NtHeaders.FileHeader.NumberOfSections); //得到节表 {$IFDEF Win64} Move(Pointer(NativeUInt(FileMemory) + SizeOf(TImageNtHeaders64) + PImageDosHeader(FileMemory)^._lfanew)^, SectionArray[0], SizeOf(TImageSectionHeader) * NtHeaders.FileHeader.NumberOfSections); {$ELSE} Move(Pointer(NativeUInt(FileMemory) + SizeOf(TImageNtHeaders32) + PImageDosHeader(FileMemory)^._lfanew)^, SectionArray[0], SizeOf(TImageSectionHeader) * NtHeaders.FileHeader.NumberOfSections); {$ENDIF} if NtHeaders.OptionalHeader.AddressOfEntryPoint <> 0 then @DllMain := Pointer(NtHeaders.OptionalHeader.AddressOfEntryPoint + NativeUInt(LoadMem)); //循环加载区块 for I := 0 to NtHeaders.FileHeader.NumberOfSections - 1 do begin //未初始化数据区段的计算方式,参考自武稀松的MemoryModule pgsize := SectionArray[I].SizeOfRawData; if pgsize = 0 then begin if (SectionArray[I].Characteristics and IMAGE_SCN_CNT_INITIALIZED_DATA) = IMAGE_SCN_CNT_INITIALIZED_DATA then begin pgsize := NtHeaders.OptionalHeader.SizeOfInitializedData; end else if (SectionArray[I].Characteristics and IMAGE_SCN_CNT_UNINITIALIZED_DATA) = IMAGE_SCN_CNT_UNINITIALIZED_DATA then begin pgsize := NtHeaders.OptionalHeader.SizeOfUninitializedData; end end; //注意未初始化的数据的SizeOfRawData为0,需要计算。 Move(Pointer(NativeUInt(FileMemory) + SectionArray[I].PointerToRawData)^,Pointer(NativeUInt(LoadMem) + SectionArray[I].VirtualAddress)^,pgsize); end; //判断是否有导入表 //提示:这里其实可以不必判断,因为NT系统要求至少有一个导入表项。 if (NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress <> 0) and (NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size <> 0) then begin //获取导入表指针 ImportHeader := Pointer(NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + NativeUInt(LoadMem)); //循环遍历指针 while (ImportHeader.FirstThunk <> 0) and (ImportHeader.Name <> 0) do begin //加载DLL ImportDllHandle := LoadLibraryA(PAnsiChar(Pointer(ImportHeader.Name + NativeUInt(LoadMem)))); //加载失败则失败 if ImportDllHandle = 0 then goto OVER; //这里会有加载失败的情况,暂时不做处理,自己必须进行处理。 //获取FirstThunk数组的指针 ThunkP := Pointer(ImportHeader.FirstThunk + NativeUInt(LoadMem)); WriteThunkP := Pointer(ImportHeader.FirstThunk + NativeUInt(LoadMem)); //TimeDateStamp为-1时,使用Characteristics作为绑定地址 参考自:MemLibrary if ImportHeader.TimeDateStamp = -1 then ThunkP := Pointer(ImportHeader.Characteristics + NativeUInt(LoadMem)) else ThunkP := WriteThunkP; while ThunkP._Function <> 0 do begin {$IFDEF Win64} if (ThunkP._Function and $8000000000000000) <> 0 then {$ELSE} if (ThunkP._Function and $80000000) <> 0 then {$ENDIF} begin WriteThunkP._Function := NativeUInt(GetMemoryLibraryProcAddress(ImportDllHandle, PAnsiChar(Cardinal(ThunkP._Function) and $FFFF))); end else begin //这个name前面有2个字节的头,要加2才能得到正确的字符串 WriteThunkP._Function := NativeUInt(GetMemoryLibraryProcAddress(ImportDllHandle, PAnsiChar(Pointer(NativeUInt(LoadMem) + ThunkP.ForwarderString + 2)))); end; ThunkP:= Pointer(NativeUInt(Pointer(ThunkP)) + SizeOf(TImageThunkData)); WriteThunkP := Pointer(NativeUInt(Pointer(WriteThunkP)) + SizeOf(TImageThunkData)); end; ImportHeader := Pointer(NativeUInt(ImportHeader) + SizeOf(_IMAGE_IMPORT_DESCRIPTOR)); end; end; //重定位表 //如果存在重定位表且需要重定位则进行重定位 //注意:由于NT中PE的重定位表并不是必须存在,为兼容加壳后的没有重定位表存在的PE结构,这里不强制要求进行重定位。 if (NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> 0) and (NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size <> 0) and not IsImageBaseLoad then begin //获取重定位结构指针 BaseRelocation := Pointer(NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + NativeUInt(LoadMem)); while BaseRelocation.VirtualAddress <> 0 do begin //计算重定位数组数量 BaseNumber := (BaseRelocation.SizeOfBlock - SizeOf(_IMAGE_BASE_RELOCATION)) div $2; BaseItem := Pointer(NativeUInt(BaseRelocation) + SizeOf(_IMAGE_BASE_RELOCATION)); for i := 0 to BaseNumber - 1 do begin {$IFDEF Win64} if ((WORD(BaseItem^) and $A000) = $A000) then begin P := Pointer((WORD(BaseItem^) mod $A000 + BaseRelocation.VirtualAddress) + NativeUInt(LoadMem)); //如果重定位目标指针无效则忽略 if Assigned(P) then NativeUInt(P^) := NativeUInt(p^) - ImageBase + NativeUInt(LoadMem); end; {$ELSE} if ((WORD(BaseItem^) and $3000) = $3000) then begin P := Pointer((WORD(BaseItem^) mod $3000 + BaseRelocation.VirtualAddress) + NativeUInt(LoadMem)); //如果重定位目标指针无效则忽略 if Assigned(P) then NativeUInt(P^) := NativeUInt(p^) - ImageBase + NativeUInt(LoadMem); end; {$ENDIF} NativeUInt(BaseItem) := NativeUInt(BaseItem) + SizeOf(WORD); end; NativeUInt(BaseRelocation) := NativeUInt(BaseRelocation) + BaseRelocation.SizeOfBlock; end; end; //异常表 //修改加载地址 PImageNtHeaders(Pointer(NativeUInt(LoadMem) + PImageDosHeader(LoadMem)^._lfanew)).OptionalHeader.ImageBase := NativeUInt(LoadMem); {$IFDEF Win64} //64位需要注册异常结构 参考:Engine、安全客、msdn、MahdiSafsafi/DebugEngine @RtlAddFunctionTable := GetMemoryLibraryProcAddress(GetModuleHandle('Kernel32.dll'),'RtlAddFunctionTable'); //如果api获取成功则开始注册 if Assigned(RtlAddFunctionTable) then begin //判断是否有异常表 if PImageNtHeaders(Pointer(NativeUInt(LoadMem) + PImageDosHeader(LoadMem)^._lfanew)).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]. VirtualAddress <> 0 then begin PruntIME := Pointer(PImageNtHeaders(Pointer(NativeUInt(LoadMem) + PImageDosHeader(LoadMem)^._lfanew)).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]. VirtualAddress + NativeUInt(LoadMem)); PruntIMENumber :=PImageNtHeaders(Pointer(NativeUInt(LoadMem) + PImageDosHeader(LoadMem)^._lfanew)).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size div sizeof(TRUNTIME_FUNCTION); RtlAddFunctionTable(PruntIME,PruntIMENumber,dword64(LoadMem)); end; end; {$ENDIF} //去掉所有页执行权限 VirtualProtect(LoadMem,NtHeaders.OptionalHeader.SizeOfHeaders,PAGE_READWRITE,&protect); //按照权限进行设置,参考自武稀松的MemoryModule for I := 0 to NtHeaders.FileHeader.NumberOfSections - 1 do begin protectvalue := 0; //设置权限,武大的算法写错了! if ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_EXECUTE) = IMAGE_SCN_MEM_EXECUTE) and ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_WRITE) = IMAGE_SCN_MEM_WRITE) and ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_READ) = IMAGE_SCN_MEM_READ) then protectvalue := PAGE_EXECUTE_READWRITE else if ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_EXECUTE) = IMAGE_SCN_MEM_EXECUTE) and ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_READ) = IMAGE_SCN_MEM_READ) then protectvalue := PAGE_EXECUTE_READ else if ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_EXECUTE) = IMAGE_SCN_MEM_EXECUTE) and ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_WRITE) = IMAGE_SCN_MEM_WRITE) and ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_READ) = IMAGE_SCN_MEM_READ) then protectvalue := PAGE_EXECUTE_WRITECOPY else begin if ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_READ) = IMAGE_SCN_MEM_READ) and ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_WRITE) = IMAGE_SCN_MEM_WRITE) then protectvalue := PAGE_READWRITE else if ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_READ) = IMAGE_SCN_MEM_READ) then protectvalue := PAGE_READONLY else if ((SectionArray[I].Characteristics and IMAGE_SCN_MEM_WRITE) = IMAGE_SCN_MEM_WRITE) then protectvalue := PAGE_WRITECOPY; end; { //错因:PAGE_XXXX并非位标志符,很多标识符是功能冲突而位不冲突,如果冲突就会设置失败! if (SectionArray[I].Characteristics and IMAGE_SCN_MEM_EXECUTE) = IMAGE_SCN_MEM_EXECUTE then begin protectvalue := protectvalue or PAGE_EXECUTE; end; if (SectionArray[I].Characteristics and IMAGE_SCN_MEM_READ) = IMAGE_SCN_MEM_READ then begin protectvalue := protectvalue or PAGE_EXECUTE_READ; end; if (SectionArray[I].Characteristics and IMAGE_SCN_MEM_WRITE) = IMAGE_SCN_MEM_WRITE then begin protectvalue := protectvalue or PAGE_WRITECOPY; end; if (SectionArray[I].Characteristics and IMAGE_SCN_MEM_NOT_CACHED) = IMAGE_SCN_MEM_NOT_CACHED then begin protectvalue := protectvalue or PAGE_NOCACHE; end; } pgsize := SectionArray[I].SizeOfRawData; if pgsize = 0 then begin if (SectionArray[I].Characteristics and IMAGE_SCN_CNT_INITIALIZED_DATA) = IMAGE_SCN_CNT_INITIALIZED_DATA then begin pgsize := NtHeaders.OptionalHeader.SizeOfInitializedData; end else if (SectionArray[I].Characteristics and IMAGE_SCN_CNT_UNINITIALIZED_DATA) = IMAGE_SCN_CNT_UNINITIALIZED_DATA then begin pgsize := NtHeaders.OptionalHeader.SizeOfUninitializedData; end end; //对齐 if pgsize mod NtHeaders.OptionalHeader.SectionAlignment > 0 then pgsize := ((pgsize div NtHeaders.OptionalHeader.SectionAlignment) + 1) * NtHeaders.OptionalHeader.SectionAlignment; if pgsize > 0 then begin OutputDebugString(PWideChar(Format('设置地址:%Xd 设置大小:%Xd 结束地址:%Xd', [SectionArray[I].VirtualAddress + NativeUInt(LoadMem), pgsize, SectionArray[I].VirtualAddress + NativeUInt(LoadMem) + pgsize]))); if VirtualProtect(Pointer(SectionArray[I].VirtualAddress + NativeUInt(LoadMem)), pgsize, protectvalue, oldProtect) then OutputDebugString('设置成功!'); end; end; //全部完成后,返回加载指针为句柄 //存在的问题:带压缩的VMP加载失败,不压缩可以加载 if NtHeaders.OptionalHeader.AddressOfEntryPoint <> 0 then DllMain(Result, DLL_PROCESS_ATTACH,nil); //返回句柄 Result := THandle(LoadMem); OVER: SetLength(SectionArray,0); end; function GetMemoryLibraryProcAddress(Const MemoryLibrary:THandle;const API:PAnsiChar):Pointer; var Memory:Pointer; NTHeader:PImageNtHeaders; ExportHeader:PImageExportDirectory; ExporFunPointer:PDWORD; ExporNamePointer:PDWORD; NameOrdinalsPointer:PWORD; FindendOrdinal:Integer; ExporFunStr:PAnsiChar; //为了避免申请内存,也是拼了 ExporFunDllName:array [0..255] of AnsiChar; ExporFunAPIName:array [0..255] of AnsiChar; TmpBoolean :Boolean; i,dlllength: Integer; begin Result := nil; Memory := Pointer(MemoryLibrary); NTHeader := PImageNtHeaders(Pointer(PImageDosHeader(Memory)._lfanew + NativeUInt(Memory))); if (NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress <> 0) and (NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size <> 0) then begin ExportHeader := PImageExportDirectory(Pointer(NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + NativeUInt(Memory))); ExporFunPointer := Pointer(ExportHeader.AddressOfFunctions + NativeUInt(Memory)); ExporNamePointer := Pointer(ExportHeader.AddressOfNames + NativeUInt(Memory)); NameOrdinalsPointer := Pointer(ExportHeader.AddressOfNameOrdinals + NativeUInt(Memory)); FindendOrdinal := -1; if (NativeUInt(API) > $FFFF) then begin for i := 0 to exportheader.NumberOfNames - 1 do begin // OutputDebugStringA(Pointer(ExporNamePointer^ + NativeUInt(Memory))); if StrIComp(PAnsiChar(ExporNamePointer^ + NativeUInt(Memory)),API) = 0 then begin FindendOrdinal := NameOrdinalsPointer^; Break; end; NativeUInt(ExporNamePointer) := NativeUInt(ExporNamePointer) + SizeOf(dword); NativeUInt(NameOrdinalsPointer) := NativeUInt(NameOrdinalsPointer) + SizeOf(Word); end; end else FindendOrdinal := DWord(API) - ExportHeader.Base; {减法!!不是加法} if FindendOrdinal = -1 then Exit; NativeUInt(ExporFunPointer) := NativeUInt(ExporFunPointer) + (SizeOf(DWORD) * FindendOrdinal); //两种类型,需要处理导出表link if (NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress > NativeUInt(ExporFunPointer^)) or (NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size < NativeUInt(ExporFunPointer^))then Result := Pointer(NativeUInt(ExporFunPointer^) + NativeUInt(Memory)) else begin Result := GetProcAddress(MemoryLibrary,API); if Result = nil then begin //处理导出表指针问题 //强撸字符串 ExporFunStr := PAnsiChar(NativeUInt(ExporFunPointer^) + NativeUInt(Memory)); //格式:DLLNAME.API i:= 0; dlllength:= 0; TmpBoolean := False; while Byte(ExporFunStr[i]) <> 0 do begin if (Byte(ExporFunStr[i]) <> 46) and (not TmpBoolean) then begin ExporFunDllName[I] := ExporFunStr[i]; end else if (Byte(ExporFunStr[i]) = 46) and (not TmpBoolean) then begin TmpBoolean := True; dlllength := i; end else begin ExporFunAPIName[I - 1 - dlllength] := ExporFunStr[i]; end; I:=I+1; if i > 255 then Break; end; Byte(ExporFunAPIName[i]) := 0; //拼接字符串 if dlllength > 1 then begin Byte(ExporFunDllName[dlllength]) := 46; Byte(ExporFunDllName[dlllength + 1]) := 68; Byte(ExporFunDllName[dlllength + 2]) := 76; Byte(ExporFunDllName[dlllength + 3]) := 76; Byte(ExporFunDllName[dlllength + 4]) := 0; Result := GetProcAddress(LoadLibraryA(ExporFunDllName),ExporFunAPIName); if Result = nil then begin OutputDebugString('发现异常的搜索api'); OutputDebugStringA(api); Exit; end; end; // 不管了,瞎构造的谁也管不了 end; end; end; end; end.