function LoadLibraryEx(
const fsLibrary: TStream; dwProcessID: DWord): Boolean;
stdcall;
type
TRelocBlock =
packed record
dwAddress: DWord;
dwSize: DWord;
end;
PRelocBlock = ^TRelocBlock;
TImportBlock =
packed record
dwCharacteristics: DWord;
dwTimeDateStamp: DWord;
dwForwarderChain: DWord;
dwName: DWord;
pFirstThunk: Pointer;
end;
PImportBlock = ^TImportBlock;
TImportNameBlock =
packed record
wHint: Word;
pName: PChar;
end;
PImportNameBlock = ^TImportNameBlock;
procedure ChangeReloc(POrigBase, PBaseTemp, PReloc, PBaseTarget: Pointer; dwRelocSize: DWord);
stdcall;
var
pCurrentRelocBlock: PRelocBlock;
RelocCount: DWord;
PCurrentStart: PWord;
i: Integer;
pRelocAddress: PInteger;
iDif: Integer;
begin
pCurrentRelocBlock := PReloc;
iDif := Integer(PBaseTarget) - Integer(POrigBase);
PCurrentStart := Pointer(Integer(PReloc) + 8);
while (
not isBadReadPtr(pCurrentRelocBlock, SizeOf(TRelocBlock)))
and
(
not isBadReadPtr(pCurrentStart, SizeOf(Pointer)))
and
(DWord(pCurrentRelocBlock) < DWord(pReloc) + dwRelocSize)
do begin
RelocCount := (pCurrentRelocBlock^.dwSize - 8)
div SizeOf(Word);
for i := 0
to RelocCount - 1
do begin
if (
not isBadReadPtr(pCurrentStart, SizeOf(Pointer)))
and
(PCurrentStart^
xor $3000 < $1000)
then begin
pRelocAddress := Pointer(pCurrentRelocBlock^.dwAddress + PCurrentStart^
mod $3000 + DWord(PBaseTemp));
if (
not isBadWritePtr(pRelocAddress, SizeOf(Integer)))
then
pRelocAddress^ := pRelocAddress^ + iDif;
end;
PCurrentStart := Pointer(DWord(PCurrentStart) + SizeOf(Word));
end;
pCurrentRelocBlock := Pointer(PCurrentStart);
pCurrentStart := Pointer(DWord(PCurrentStart) + 8);
end;
end;
procedure CreateImportTable(pLibraryHandle, pImportTable: pointer);
stdcall;
var
pIBlock: PImportBlock;
pThunksRead: PDWord;
pThunksWrite: PDWord;
pDllName: PChar;
dwLibraryHandle: DWord;
dwOldProtect: DWord;
begin
pIBlock := pImportTable;
while (
not isBadReadPtr(pIBlock, SizeOf(TImportBlock)))
and
(pIBlock^.pFirstThunk <>
nil)
and (pIBlock^.dwName <> 0)
do begin
pDllName := Pointer(DWord(pLibraryHandle) + DWord(pIBlock^.dwName));
if (
not isBadReadPtr(pDllName, 4))
then begin
dwLibraryHandle := LoadLibrary(pDllName);
pThunksRead := Pointer(DWord(pIBlock^.pFirstThunk) + DWord(pLibraryHandle));
pThunksWrite := pThunksRead;
if (DWord(pIBlock^.dwTimeDateStamp) = $FFFFFFFF)
then
pThunksRead := Pointer(DWord(pIBlock^.dwCharacteristics) + DWord(pLibraryHandle));
while (
not isBadReadPtr(pThunksRead, SizeOf(DWord)))
and
(
not isBadReadPtr(pThunksWrite, SizeOf(Word)))
and
(pThunksRead^ <> 0)
do begin
if VirtualProtect(pThunksWrite, SizeOf(DWord), PAGE_EXECUTE_READWRITE, dwOldProtect)
then begin
if (DWord(pThunksRead^)
and $80000000 <> 0)
then
pThunksWrite^ := DWord(GetProcAddress(dwLibraryHandle, PChar(pThunksRead^
and $FFFF)))
else
pThunksWrite^ := DWord(GetProcAddress(dwLibraryHandle, PChar(DWord(pLibraryHandle) + pThunksRead^ + SizeOf(Word))));
VirtualProtect(pThunksWrite, SizeOf(DWord), dwOldProtect, dwOldProtect);
end;
Inc(pThunksRead);
Inc(pThunksWrite);
end;
end;
pIBlock := Pointer(DWord(pIBlock) + SizeOf(TImportBlock));
end;
end;
procedure StartDll(dwDllHandle: DWord);
stdcall;
asm
MOV EBX, DWORD PTR [dwDllHandle]
CALL @@J
@@J:
POP EAX
AND EAX, $FFFFF000
MOV EAX, DWORD PTR [EAX]
PUSH 0
PUSH DLL_PROCESS_ATTACH
PUSH EBX
CALL EAX
end;
procedure EndDll;
asm end;
var
DllMain:
function(dwHandle, dwReason, dwReserved: DWord): DWord;
stdcall;
IDH: PImageDosHeader;
INH: PImageNtHeaders;
SEC: PImageSectionHeader;
dwSecCount: DWord;
dwmemsize: DWord;
i: Integer;
pFileMem: Pointer;
pMemLocal: Pointer;
pMemProcess: Pointer;
hProcessHandle: THandle;
dwWritten: DWord;
hThread: THandle;
dwThreadID: DWord;
ThreadStart:
function(_Param: Pointer): Integer;
stdcall;
begin
Result := False;
// ProcessHandle holen um Speicher für DLL zu allozieren und RemoteThread zu starten
hProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
if hProcessHandle <> 0
then begin
// für den Stream speicher holen um leichter auf die Daten zuzugreifen
pFileMem := VirtualAlloc(
nil, fsLibrary.Size, MEM_COMMIT
or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pFileMem <>
nil)
then begin
fsLibrary.Position := 0;
fsLibrary.
Read(pFileMem^, fsLibrary.Size);
IDH := pFileMem;
// ImageDosHeader gültig?
if (
not isBadReadPtr(IDH, SizeOf(TImageDosHeader)))
and
(IDH^.e_magic = IMAGE_DOS_SIGNATURE)
then begin
// ImageNtHeader gültig?
INH := pointer(cardinal(pFileMem) + cardinal(IDH^._lfanew));
if (
not isBadReadPtr(INH, SizeOf(TImageNtHeaders)))
and
(INH^.Signature = IMAGE_NT_SIGNATURE)
then begin
SEC := Pointer(Integer(INH) + SizeOf(TImageNtHeaders));
// Virtuelle größe der Dll/Exe ermitteln
dwMemSize := INH^.OptionalHeader.SizeOfImage;
if (dwMemSize <> 0)
then begin
// Lokalen speicher für die DLL holen (wir bereiten die in userem Prozess vor)
pMemLocal := VirtualAlloc(
nil, dwMemSize, MEM_COMMIT
or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pMemLocal <>
nil)
then begin
// Speicher aus dem Fremdprozess holen (indem wir die dll nachher kopieren und ausführen)
pMemProcess := VirtualAllocEx(hProcessHandle,
nil, dwMemSize+$1000, MEM_COMMIT
or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if pMemProcess <>
nil then begin
// jede Sektion and die richtige Adresse kopieren
dwSecCount := INH^.FileHeader.NumberOfSections;
CopyMemory(pMemLocal, IDH, DWord(SEC) - DWord(IDH) + dwSecCount * SizeOf(TImageSectionHeader));
for i := 0
to dwSecCount - 1
do begin
CopyMemory(Pointer(DWord(pMemLocal) + SEC^.VirtualAddress),
Pointer(DWord(pFileMem) + DWord(SEC^.PointerToRawData)),
SEC^.SizeOfRawData);
SEC := Pointer(Integer(SEC) + SizeOf(TImageSectionHeader));
end;
// Adressen der Dll für den Zielprozess anpassen
if (INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> 0)
then begin
ChangeReloc(Pointer(INH^.OptionalHeader.ImageBase),
pMemLocal,
Pointer(DWord(pMemLocal) + INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress),
Pointer(pMemProcess),
INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
end;
// die Importtabelle anpassen, die Adressen holen wir lokal aus unserem Prozess
// sollte die Dll nicht im Zielprozess geladen sein -> crash
// theoretisch kann man diese auch aus dem Zielprozess ermitteln
// und fehlende Dlls dort ebenfalls nachladen
// aber jetzt nicht hier behandelt, da dll die in einen Fremdprozess injeziert werden
// IMMER nur ntdll/kernel32/(user32 falls Prozess mit Fenster) Funktionen benutzen sollen
CreateImportTable(pMemLocal, Pointer(DWord(pMemLocal) + INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));
// bei DLL-Main einen Thread starten
@DllMain := Pointer(INH^.OptionalHeader.AddressOfEntryPoint + DWord(pMemProcess));
if WriteProcessMemory(hProcessHandle, pMemProcess, pMemLocal, dwMemSize, dwWritten)
and (dwWritten = dwMemSize)
and
WriteProcessMemory(hProcessHandle, Pointer(DWord(pMemProcess)+dwMemSize), @@DllMain, 4, dwWritten)
and (dwWritten = 4)
and
WriteProcessMemory(hProcessHandle, Pointer(DWord(pMemProcess)+dwMemSize+4), @StartDll, Integer(@EndDll)-Integer(@StartDll), dwWritten)
then begin
@ThreadStart := Pointer(DWord(pMemProcess)+dwMemSize+4);
//ThreadStart(nil);
hThread := CreateRemoteThread(hProcessHandle,
nil, 0, @ThreadStart,
nil, 0, dwThreadID);
if hThread <> 0
then begin
Result := True;
CloseHandle(hThread);
end;
end else begin
VirtualFreeEx(hProcessHandle, pMemProcess, dwMemSize+$1000, MEM_DECOMMIT);
end;
end;
end;
VirtualFree(pMemLocal, dwMemSize, MEM_DECOMMIT);
end;
end;
VirtualFree(pFileMem, fsLibrary.Size, MEM_DECOMMIT);
end;
end;
CloseHandle(hProcessHandle);
end;
end;