We don't display ads so we rely on your Bitcoin donations to 1KWEk9QaiJb2NwP5YFmR24LyUBa4JyuKqZ
Post date: Jul 11, 2010 10:06:28 PM
This unit by StTwister allows you to unhook apis for local and remote processes. It also contains functions which can be used as an alternative to the GetProcAddress Api.
StTwister on experts exchange:
http://www.experts-exchange.com/M_2616736.html
The unit is well-commented for your understanding. As you can read in the comments, this unit was written after Aphex's afxcodehook and Leak Test (soon to be published here).
{*****************************************************************}
{ }
{ SttUnhooker unit by StTwister }
{ http://gateofgod.com }
{ StTwister2003@yahoo.co.uk }
{ }
{ Unhooks APIs for both local and remote processes }
{ }
{*****************************************************************}
unit untSttUnhooker;
// if the DISPLAY_ERRORS flag is enabled, any error/warning will show up
{$DEFINE DISPLAY_ERRORS}
interface
uses
Windows;
function UnHookAPI(strModuleName, strFuncName: string): boolean;
function UnHookAPIEx(hProcess: THandle; strModuleName, strFuncName: string): boolean;
function GetRealProcAddress(strModuleName, strFuncName: string): Pointer;
function GetRealProcAddressEx(hProcess: THandle; strModuleName, strFuncName: string): Pointer;
var
pCreateRemoteThread: function(hProcess: THandle; lpThreadAttributes: Pointer; dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine; lpParameter: Pointer; dwCreationFlags: DWORD; var lpThreadId: DWORD): THandle; stdcall;
pVirtualAllocEx: function(hProcess: THandle; lpAddress: Pointer; dwSize, flAllocationType: DWORD; flProtect: DWORD): Pointer; stdcall;
pWriteProcessMemory: function(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesWritten: DWORD): BOOL; stdcall;
implementation
type
TSmallArray = array[1..20] of byte;
PRemoteInfo = ^TRemoteInfo;
TRemoteInfo = record
pGetModuleHandle: function(lpModuleName: PAnsiChar): cardinal; stdcall;
pGetProcAddress: function(hModule: cardinal; lpProcName: PAnsiChar): Pointer; stdcall;
pGetModuleFileName: function(hModule: cardinal; lpFilename: PAnsiChar; nSize: cardinal): cardinal; stdcall;
lpModuleName, lpFuncName, lpFilename: PChar;
lpFuncAddress: Pointer;
dwLength: DWORD;
end;
const
Opcodes1: array [0..255] of word =
(
(16913),(17124),(8209),(8420),(33793),(35906),(0),(0),(16913),(17124),(8209),(8420),(33793),(35906),(0),(0),(16913),
(17124),(8209),(8420),(33793),(35906),(0),(0),(16913),(17124),(8209),(8420),(33793),(35906),(0),(0),(16913),
(17124),(8209),(8420),(33793),(35906),(0),(32768),(16913),(17124),(8209),(8420),(33793),(35906),(0),(32768),(16913),
(17124),(8209),(8420),(33793),(35906),(0),(32768),(529),(740),(17),(228),(1025),(3138),(0),(32768),(24645),
(24645),(24645),(24645),(24645),(24645),(24645),(24645),(24645),(24645),(24645),(24645),(24645),(24645),(24645),(24645),(69),
(69),(69),(69),(69),(69),(69),(69),(24645),(24645),(24645),(24645),(24645),(24645),(24645),(24645),(0),
(32768),(228),(16922),(0),(0),(0),(0),(3072),(11492),(1024),(9444),(0),(0),(0),(0),(5120),
(5120),(5120),(5120),(5120),(5120),(5120),(5120),(5120),(5120),(5120),(5120),(5120),(5120),(5120),(5120),(1296),
(3488),(1296),(1440),(529),(740),(41489),(41700),(16913),(17124),(8209),(8420),(17123),(8420),(227),(416),(0),
(57414),(57414),(57414),(57414),(57414),(57414),(57414),(32768),(0),(0),(0),(0),(0),(0),(32768),(33025),
(33090),(769),(834),(0),(0),(0),(0),(1025),(3138),(0),(0),(32768),(32768),(0),(0),(25604),
(25604),(25604),(25604),(25604),(25604),(25604),(25604),(27717),(27717),(27717),(27717),(27717),(27717),(27717),(27717),(17680),
(17824),(2048),(0),(8420),(8420),(17680),(19872),(0),(0),(2048),(0),(0),(1024),(0),(0),(16656),
(16800),(16656),(16800),(33792),(33792),(0),(32768),(8),(8),(8),(8),(8),(8),(8),(8),(5120),
(5120),(5120),(5120),(33793),(33858),(1537),(1602),(7168),(7168),(0),(5120),(32775),(32839),(519),(583),(0),
(0),(0),(0),(0),(0),(8),(8),(0),(0),(0),(0),(0),(0),(16656),(416)
);
Opcodes2: array [0..255] of word =
(
(280),(288),(8420),(8420),(65535),(0),(0),(0),(0),(0),(65535),(65535),(65535),(272),(0),(1325),(63),
(575),(63),(575),(63),(63),(63),(575),(272),(65535),(65535),(65535),(65535),(65535),(65535),(65535),(16419),
(16419),(547),(547),(65535),(65535),(65535),(65535),(63),(575),(47),(575),(61),(61),(63),(63),(0),
(32768),(32768),(32768),(0),(0),(65535),(65535),(65535),(65535),(65535),(65535),(65535),(65535),(65535),(65535),(8420),
(8420),(8420),(8420),(8420),(8420),(8420),(8420),(8420),(8420),(8420),(8420),(8420),(8420),(8420),(8420),(16935),
(63),(63),(63),(63),(63),(63),(63),(63),(63),(63),(63),(63),(63),(63),(63),(237),
(237),(237),(237),(237),(237),(237),(237),(237),(237),(237),(237),(237),(237),(101),(237),(1261),
(1192),(1192),(1192),(237),(237),(237),(0),(65535),(65535),(65535),(65535),(65535),(65535),(613),(749),(7168),
(7168),(7168),(7168),(7168),(7168),(7168),(7168),(7168),(7168),(7168),(7168),(7168),(7168),(7168),(7168),(16656),
(16656),(16656),(16656),(16656),(16656),(16656),(16656),(16656),(16656),(16656),(16656),(16656),(16656),(16656),(16656),(0),
(0),(32768),(740),(18404),(17380),(49681),(49892),(0),(0),(0),(17124),(18404),(17380),(32),(8420),(49681),
(49892),(8420),(17124),(8420),(8932),(8532),(8476),(65535),(65535),(1440),(17124),(8420),(8420),(8532),(8476),(41489),
(41700),(1087),(548),(1125),(9388),(1087),(33064),(24581),(24581),(24581),(24581),(24581),(24581),(24581),(24581),(65535),
(237),(237),(237),(237),(237),(749),(8364),(237),(237),(237),(237),(237),(237),(237),(237),(237),
(237),(237),(237),(237),(237),(63),(749),(237),(237),(237),(237),(237),(237),(237),(237),(65535),
(237),(237),(237),(237),(237),(237),(237),(237),(237),(237),(237),(237),(237),(237),(0)
);
Opcodes3: array [0..9] of array [0..15] of word =
(
((1296),(65535),(16656),(16656),(33040),(33040),(33040),(33040),(1296),(65535),(16656),(16656),(33040),(33040),(33040),(33040)),
((3488),(65535),(16800),(16800),(33184),(33184),(33184),(33184),(3488),(65535),(16800),(16800),(33184),(33184),(33184),(33184)),
((288),(288),(288),(288),(288),(288),(288),(288),(54),(54),(48),(48),(54),(54),(54),(54)),
((288),(65535),(288),(288),(272),(280),(272),(280),(48),(48),(0),(48),(0),(0),(0),(0)),
((288),(288),(288),(288),(288),(288),(288),(288),(54),(54),(54),(54),(65535),(0),(65535),(65535)),
((288),(65535),(288),(288),(65535),(304),(65535),(304),(54),(54),(54),(54),(0),(54),(54),(0)),
((296),(296),(296),(296),(296),(296),(296),(296),(566),(566),(48),(48),(566),(566),(566),(566)),
((296),(65535),(296),(296),(272),(65535),(272),(280),(48),(48),(48),(48),(48),(48),(65535),(65535)),
((280),(280),(280),(280),(280),(280),(280),(280),(566),(566),(48),(566),(566),(566),(566),(566)),
((280),(65535),(280),(280),(304),(296),(304),(296),(48),(48),(48),(48),(0),(54),(54),(65535))
);
function LowerCase(const S: string): string;
var
Ch: Char;
i: Integer;
Source, Dest: PChar;
begin
i := Length(S);
SetLength(Result, i);
Source := PChar(S);
Dest := PChar(Result);
while i <> 0 do
begin
Ch := Source^;
if (Ch >= 'A') and (Ch <= 'Z') then
Inc(Ch, 32);
Dest^ := Ch;
Inc(Source);
Inc(Dest);
Dec(i);
end;
end;
// displays an error message if the DISPLAY_ERRORS flag is enabled
procedure Error(strError: string);
begin
{$IFDEF DISPLAY_ERRORS}
// change this line if you want the error to appear in a different way than a messagebox.
// For example, you could use Write() in console apps or append to a log file
MessageBox(0, PChar(strError), 'Error', MB_ICONERROR);
{$ENDIF}
end;
// as small LDE to calculate the length of CPU instructions
// taken from Aphex's afxCodeHook unit (http://www.iamaphex.com)
function SizeOfCode(Code: pointer): longword;
var
Opcode: word;
Modrm: byte;
Fixed, AddressOveride: boolean;
Last, OperandOveride, Flags, Rm, Size, Extend: longword;
begin
try
Last := longword(Code);
if Code <> nil then
begin
AddressOveride := False;
Fixed := False;
OperandOveride := 4;
Extend := 0;
repeat
Opcode := byte(Code^);
Code := pointer(longword(Code) + 1);
if Opcode = $66 then
begin
OperandOveride := 2;
end
else if Opcode = $67 then
begin
AddressOveride := True;
end
else
begin
if not ((Opcode and $E7) = $26) then
begin
if not (Opcode in [$64..$65]) then
begin
Fixed := True;
end;
end;
end;
until Fixed;
if Opcode = $0f then
begin
Opcode := byte(Code^);
Flags := Opcodes2[Opcode];
Opcode := Opcode + $0f00;
Code := pointer(longword(Code) + 1);
end
else
begin
Flags := Opcodes1[Opcode];
end;
if ((Flags and $0038) <> 0) then
begin
Modrm := byte(Code^);
Rm := Modrm and $7;
Code := pointer(longword(Code) + 1);
case (Modrm and $c0) of
$40: Size := 1;
$80:
begin
if AddressOveride then
begin
Size := 2;
end
else
Size := 4;
end;
else
begin
Size := 0;
end;
end;
if not (((Modrm and $c0) <> $c0) and AddressOveride) then
begin
if (Rm = 4) and ((Modrm and $c0) <> $c0) then
begin
Rm := byte(Code^) and $7;
end;
if ((Modrm and $c0 = 0) and (Rm = 5)) then
begin
Size := 4;
end;
Code := pointer(longword(Code) + Size);
end;
if ((Flags and $0038) = $0008) then
begin
case Opcode of
$f6: Extend := 0;
$f7: Extend := 1;
$d8: Extend := 2;
$d9: Extend := 3;
$da: Extend := 4;
$db: Extend := 5;
$dc: Extend := 6;
$dd: Extend := 7;
$de: Extend := 8;
$df: Extend := 9;
end;
if ((Modrm and $c0) <> $c0) then
begin
Flags := Opcodes3[Extend][(Modrm shr 3) and $7];
end
else
begin
Flags := Opcodes3[Extend][((Modrm shr 3) and $7) + 8];
end;
end;
end;
case (Flags and $0C00) of
$0400: Code := pointer(longword(Code) + 1);
$0800: Code := pointer(longword(Code) + 2);
$0C00: Code := pointer(longword(Code) + OperandOveride);
else
begin
case Opcode of
$9a, $ea: Code := pointer(longword(Code) + OperandOveride + 2);
$c8: Code := pointer(longword(Code) + 3);
$a0..$a3:
begin
if AddressOveride then
begin
Code := pointer(longword(Code) + 2)
end
else
begin
Code := pointer(longword(Code) + 4);
end;
end;
end;
end;
end;
end;
Result := longword(Code) - Last;
except
Result := 0;
end;
end;
// unhooks some APIs used for remote unhooking, so FWs don't catch it
procedure InitInjectionAPIs;
begin
pVirtualAllocEx := GetRealProcAddress('kernel32', 'VirtualAllocEx');
pWriteProcessMemory := GetRealProcAddress('kernel32', 'WriteProcessMemory');
pCreateRemoteThread := GetRealProcAddress('kernel32', 'CreateRemoteThread');
end;
// this is a function that will be injected in the remote thread to get the function address
// and full path to library file
procedure RemoteThread(Param: Pointer); stdcall;
begin
With TRemoteInfo(Param^) do
begin
lpFuncAddress := pGetProcAddress(pGetModuleHandle(lpModuleName), lpFuncName);
dwLength := pGetModuleFileName(pGetModuleHandle(lpModuleName), lpFileName, 4096);
end;
end;
// null function used to calculate the size of the RemoteThread function
procedure RemoteThreadEnd; stdcall;
begin
end;
function GetRemoteProcAddress(hProcess: THandle; strModuleName, strFuncName: string;
var strFileName: string): pointer;
var
RemoteInfo: TRemoteInfo;
dwBytesWritten, dwSize: DWORD;
lpRemoteInfo, lpFunc: Pointer;
TID: cardinal;
begin
// if we are unhooking local process, we don't need to inject a function
if hProcess = GetCurrentProcess then
begin
Result := GetProcAddress(GetModuleHandle(Pchar(strModuleName)), PChar(strFuncName));
SetLength(strFileName, 4096);
dwBytesWritten := GetModuleFileName(GetModuleHandle(PChar(strModuleName)), PChar(strFileName), 4096);
SetLength(strFileName, dwBytesWritten);
exit;
end;
// fill API addresses into RemoteInfo
RemoteInfo.pGetModuleHandle := GetProcAddress(GetModuleHandle('kernel32'), 'GetModuleHandleA');
RemoteInfo.pGetProcAddress := GetProcAddress(GetModuleHandle('kernel32'), 'GetProcAddress');
RemoteInfo.pGetModuleFileName := GetProcAddress(GetModuleHandle('kernel32'), 'GetModuleFileNameA');
// allocate memory for the strings in the remote process
RemoteInfo.lpModuleName := pVirtualAllocEx(hProcess, nil, Length(strModuleName)+1, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
RemoteInfo.lpFuncName := pVirtualAllocEx(hProcess, nil, Length(strFuncName)+1, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
RemoteInfo.lpFileName := pVirtualAllocEx(hProcess, nil, 4096, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// write strings to remote process
pWriteProcessMemory(hProcess, RemoteInfo.lpModuleName, PChar(strModuleName), Length(strModuleName) + 1, dwBytesWritten);
pWriteProcessMemory(hProcess, RemoteInfo.lpFuncName, PChar(strFuncName), Length(strFuncName), dwBytesWritten);
// allocate memory for the remote info structure in the remote process
lpRemoteInfo := pVirtualAllocEx(hProcess, nil, SizeOf(TRemoteInfo), MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// write remote info structure to the remote process
pWriteProcessMemory(hProcess, lpRemoteInfo, @RemoteInfo, SizeOf(RemoteInfo), dwBytesWritten);
// calculate the size of the remote function
dwSize := DWORD(@RemoteThreadEnd) - DWORD(@RemoteThread);
// allocate meory for the remote function in the remote process
lpFunc := pVirtualAllocEx(hProcess, nil, dwSize, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// write remote function to the remote process
pWriteProcessMemory(hProcess, lpFunc, @RemoteThread, dwSize, dwBytesWritten);
// execute the remote function
TID := pCreateRemoteThread(hProcess, nil, 0, lpFunc, lpRemoteInfo, 0, TID);
// wait for the remote thread to terminate
WaitForSingleObject(TID, INFINITE);
// get back the results
ReadProcessMemory(hProcess, lpRemoteInfo, @RemoteInfo, SizeOf(RemoteInfo), dwBytesWritten);
Result := RemoteInfo.lpFuncAddress;
// get the module path
SetLength(strFileName, RemoteInfo.dwLength);
ReadProcessMemory(hProcess, RemoteInfo.lpFilename, PChar(strFileName), RemoteInfo.dwLength, dwBytesWritten);
// clean up
VirtualFreeEx(hProcess, RemoteInfo.lpModuleName, Length(strModuleName)+1, MEM_RELEASE);
VirtualFreeEx(hProcess, RemoteInfo.lpFuncName, Length(strFuncName)+1, MEM_RELEASE);
VirtualFreeEx(hProcess, RemoteInfo.lpFileName, 4096, MEM_RELEASE);
VirtualFreeEx(hProcess, lpRemoteInfo, SizeOf(TRemoteInfo), MEM_RELEASE);
VirtualFreeEx(hProcess, lpFunc, dwSize, MEM_RELEASE);
end;
// read the first bytes of the function to see if it's hooked or not
function ReadFunctionBytes(hProcess: Thandle; strModuleName, strFuncName: string;
var SmallArray: TSmallArray; var lpFuncAddress: Pointer; var strFileName: string): boolean;
var
dwBytesRead: dword;
begin
Result := False;
// if module is kernel32, we don't need to inject a routine to get the function
// address since kernel32 APIs have the same address in all processes
if (LowerCase(strModuleName) = 'kernel32') or (LowerCase(strModuleName) = 'kernel32.dll') then
begin
lpFuncAddress := GetProcAddress(GetModuleHandle(PChar(strModuleName)), PChar(strFuncName));
SetLength(strFileName, 4096);
dwBytesRead := GetModuleFileName(GetModuleHandle(Pchar(strModuleName)), PChar(strFileName), 4096);
SetLength(strFileName, 4096);
end
else
begin
lpFuncAddress := GetRemoteProcAddress(hProcess, strModuleName, strFuncName, strFileName);
end;
if lpFuncAddress = nil then
begin
Error('Cannot get the address of function '+strFuncName+' function!');
exit;
end;
// read the first 20 bytes of the function to detect if function is hooked and to calculate
// how many bytes need to be rewritten
ReadProcessMemory(hProcess, lpFuncAddress, @SmallArray, SizeOf(SmallArray), dwBytesRead);
if dwBytesRead <> SizeOf(SmallArray) then
begin
Error('Cannot read from remote function address!');
exit;
end;
Result := True;
end;
// reads the original start bytes of an API directly from the library file
function ReadOriginalFunctionBytes(strModuleName, strFuncName, strFileName: string;
var SmallArray: TSmallArray; intNops: Integer): boolean;
function GetFieldOffset(const Struct; const Field): Cardinal;
begin
Result := Cardinal(@Field) - Cardinal(@Struct);
end;
// replacement of IMAGE_FIRST_SECTION macro
function GetImageFirstSection(NtHeader: PImageNtHeaders): PImageSectionHeader;
begin
Result := PImageSectionHeader(Cardinal(NtHeader) +
GetFieldOffset(NtHeader^, NtHeader^.OptionalHeader) +
NtHeader^.FileHeader.SizeOfOptionalHeader);
end;
var
hFile: THandle;
lpstrFuncName: PChar;
lpData, lpFunc: Pointer;
dwSize,dwBytesRead, dwVirtualOffset, dwPhysicalOffset, dwFuncOrdinal: DWORD;
i: Integer;
bFound: Boolean;
DosHeader: PImageDosHeader;
NTHeader: PImageNtHeaders;
ExportDir: PImageExportDirectory;
Directory: PImageDataDirectory;
SectionHeader: PImageSectionHeader;
begin
Result := False;
// open the file in read mode
hFile := CreateFile(PChar(strFileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
if hFile = INVALID_HANDLE_VALUE then
begin
Error('Cannot open file '+strModuleName+'!');
CloseHandle(hFile);
exit;
end;
// copy to memory
dwSize := GetFileSize(hFile, nil);
lpData := GetMemory(dwSize);
ReadFile(hFile, lpData^, dwSize, dwBytesRead, nil);
CloseHandle(hFile);
if dwBytesRead <> dwSize then
begin
Error('Cannot read from file '+strModuleName+'!');
FreeMem(lpData, dwSize);
exit;
end;
// load the MZ and PE headers
DosHeader := lpData;
if DosHeader.e_magic <> IMAGE_DOS_SIGNATURE then
begin
Error('Invalid MZ header in file '+strModuleName+'!');
FreeMem(lpData, dwSize);
exit;
end;
NTHeader := Pointer(Integer(lpData) + DosHeader._lfanew);
if NTHeader.Signature <> IMAGE_NT_SIGNATURE then
begin
Error('Invalid PE header in file '+strModuleName+'!');
FreeMem(lpData, dwSize);
exit;
end;
// get the export table virtual address
Directory := @NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if Directory.Size = 0 then
begin
Error('No export table found in file '+strModuleName+'!');
FreeMem(lpData, dwSize);
exit;
end;
dwVirtualOffset := Directory.VirtualAddress;
// find the section where the export table is located
// dwVirtualOffset = offset where the export table would normally reside in memory
// dwPhysicalOffset = offset where export table is located in current app memory (as if it was a file)
dwPhysicalOffset := 0;
SectionHeader := GetImageFirstSection(NtHeader);
for i := 1 to NTHeader.FileHeader.NumberOfSections do
begin
if (dwVirtualOffset >= SectionHeader.VirtualAddress) and (dwVirtualOffset < SectionHeader.VirtualAddress + SectionHeader.SizeOfRawData) then
begin
dwPhysicalOffset := SectionHeader.PointerToRawData + (dwVirtualOffset - SectionHeader.VirtualAddress);
break;
end;
SectionHeader := Pointer(DWORD(SectionHeader) + SizeOf(TImageSectionHeader));
end;
if dwPhysicalOffset = 0 then
begin
Error('Cannot find section where export table is located in file '+strModuleName+'!');
FreeMem(lpData, dwSize);
exit;
end;
ExportDir := Pointer(DWORD(lpData) + dwPhysicalOffset);
// loop through all functions to find right function (with the name strFuncName)
bFound := False;
lpFunc := Pointer(DWORD(ExportDir) + (DWORD(ExportDir.AddressOfNames) - dwVirtualOffset));
for i := 1 to ExportDir.NumberOfNames do
begin
lpstrFuncName := Pointer(DWORD(ExportDir) + (DWORD(lpFunc^) - dwVirtualOffset));
if lpstrFuncName = strFuncName then
begin
bFound := True;
break;
end;
lpFunc := Pointer(DWORD(lpFunc) + SizeOf(DWORD));
end;
if not bFound then
begin
Error('Function '+strFuncName+' not found in the export table of the file '+strModuleName+'!');
FreeMem(lpData, dwSize);
exit;
end;
// find the function ordinal associated to the function name
lpFunc := Pointer(DWORD(ExportDir) + (DWORD(ExportDir.AddressOfNameOrdinals) - dwVirtualOffset));
lpFunc := Pointer(Integer(lpFunc) + (i - 1) * SizeOf(WORD));
dwFuncOrdinal := WORD(lpFunc^) + ExportDir.Base;
if (dwFuncOrdinal < ExportDir.Base) or (dwFuncOrdinal > ExportDir.Base + ExportDir.NumberOfFunctions - 1) then
begin
Error('No function ordinal found for function '+strFuncName+' in the file '+strModuleName+'!');
FreeMem(lpData, dwSize);
exit;
end;
// get the function entry address using the function ordinal
lpFunc := Pointer(DWORD(ExportDir) + (DWORD(ExportDir.AddressOfFunctions) - dwVirtualOffset + (dwFuncOrdinal - ExportDir.Base) * SizeOf(DWORD)));
// finally we got the function address. Now we must find the coresponding section and copy
// the function code
dwPhysicalOffset := 0;
SectionHeader := GetImageFirstSection(NtHeader);
for i := 1 to NTHeader.FileHeader.NumberOfSections do
begin
if (DWORD(lpFunc^) >= SectionHeader.VirtualAddress) and (DWORD(lpFunc^) < SectionHeader.VirtualAddress + SectionHeader.SizeOfRawData) then
begin
dwPhysicalOffset := SectionHeader.PointerToRawData + (DWORD(lpFunc^) - SectionHeader.VirtualAddress);
break;
end;
SectionHeader := Pointer(DWORD(SectionHeader) + SizeOf(TImageSectionHeader));
end;
if dwPhysicalOffset = 0 then
begin
Error('Cannot find function '+strFuncName+' code in the file '+strModuleName+'!');
FreeMem(lpData, dwSize);
exit;
end;
// finally we can copy our needed data into SmallArray
CopyMemory(@SmallArray, Pointer(DWORD(lpData) + dwPhysicalOffset), intNops);
// free the loaded file
FreeMem(lpData, dwSize);
Result := True;
end;
// unhooks an API, given the module name and function name
// only unhooks overwriting/extended overwriting hooks
function UnHookAPI(strModuleName, strFuncName: string): boolean;
begin
Result := UnHookAPIEx(GetCurrentProcess, strModuleName, strFuncName);
end;
// unhooks an API of a remote process, given the modeule name and function name
// only unhooks overwriting/extended overwriting hooks
function UnHookAPIEx(hProcess: THandle; strModuleName, strFuncName: string): boolean;
var
SmallArray: TSmallArray;
intNops: Integer;
dwBytesWritten: DWORD;
lpFuncAddress: Pointer;
strFileName: string;
begin
Result := False;
// read the first 20 bytes of the function
// also gets the address of the function in the remote process and the full path to library
if not ReadFunctionBytes(hProcess, strModuleName, strFuncName, SmallArray, lpFuncAddress, strFileName) then
exit;
// if the function is hooked, it contains a JMP as the first operations
// therefore, if the first byte is not $E9, then we know the function is not hooked
if SmallArray[1] <> $E9 then
begin
Result := True;
exit;
end;
// read how many NOPs exist after the JMP so we can know how many bytes need to be rewritten
intNops := 0;
while SmallArray[6 + intNops] = $90 do
inc(intNops);
// intNops + 5 = total number of bytes that need to be rewritten
intNops := intNops + 5;
if not ReadOriginalFunctionBytes(strModuleName, strFuncName, strFileName, SmallArray, intNops) then
exit;
pWriteProcessMemory(hProcess, lpFuncAddress, @SmallArray, intNops, dwBytesWritten);
if dwBytesWritten <> DWORD(intNops) then
begin
Error('Cannot write data to remote API location');
exit;
end;
Result := True;
end;
// Creates a new function that behaves exactly like the original one, given the module name
// and function name, but without being hooked.
// Use this function rather than UnHookAPI if u want to have access to both hooked and unhooked
// functions or if u don;t want the function to be rehooked
function GetRealProcAddress(strModuleName, strFuncName: string): Pointer;
begin
Result := GetRealProcAddressEx(GetCurrentProcess, strModuleName, strFuncName);
end;
function GetRealProcAddressEx(hProcess: THandle; strModuleName, strFuncName: string): Pointer;
var
SmallArray: TSmallArray;
dwBytesWritten, dwLength, dwLen: DWORD;
lpFuncAddress, lpNewFuncAddress, lpAddr: Pointer;
strFileName: string;
begin
Result := nil;
// read the first 20 bytes of the function
// also gets the address of the function in the remote process and the full path to library
if not ReadFunctionBytes(hProcess, strModuleName, strFuncName, SmallArray, lpFuncAddress, strFileName) then
exit;
if not ReadOriginalFunctionBytes(strModuleName, strFuncName, strFileName, SmallArray, 20) then
exit;
// use a LDE to find the length of the instructions that have benn overwirtten by the JMP
lpAddr := @SmallArray;
dwLength := 0;
While dwLength < 5 do
begin
dwLen := SizeOfCode(lpAddr);
if dwLen = 0 then
exit;
dwLength := dwLength + dwLen;
lpAddr := Pointer(DWORD(lpAddr) + dwLen);
end;
// allocate memory for the new function
if @pVirtualAllocEx <> nil then
lpNewFuncAddress := pVirtualAllocEx(hProcess, nil, dwLength + 5, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE)
else
lpNewFuncAddress := VirtualAllocEx(hProcess, nil, dwLength + 5, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if lpNewFuncAddress = nil then
begin
Error('Cannot allocate memory for the new function');
exit;
end;
// after we've got the bytes that were modified, we need to link them back to the original
// function with a JMP ($E9)
SmallArray[dwLength + 1] := $E9;
DWORD(Pointer(DWORD(@SmallArray) + dwLength + 1)^) := DWORD(lpFuncAddress) - DWORD(lpNewFuncAddress) - 5;
// finally write the created function to the remote process
if @pWriteProcessMemory <> nil then
pWriteProcessMemory(hProcess, lpNewFuncAddress, @SmallArray, dwLength + 5, dwBytesWritten)
else
WriteProcessMemory(hProcess, lpNewFuncAddress, @SmallArray, dwLength + 5, dwBytesWritten);
if dwBytesWritten <> DWORD(dwLength + 5) then
begin
Error('Cannot write data to remote API location');
exit;
end;
Result := lpNewFuncAddress;
end;
initialization
InitInjectionAPIs;
end.