//去年写的一份内存加载,某人用的还不错,扔出来给有需要的人,无坑。
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.