【发布时间】:2019-06-18 08:16:17
【问题描述】:
我正在尝试在 api 挂钩中调用原始函数,以防止 LdrLoadDll() 函数注入 dll,但每次我尝试加载过滤后的不同 dll 时,应用程序崩溃并且不可能调用原始函数。似乎我什么时候会在钩子之前保存“original_function”时做错了。
我在 Windows 7 x64 上进行测试,在 32 位应用程序中注入 32 位 dll(代码如下)。
如何解决这个问题?
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Windows,
SysUtils;
type
NTSTATUS = Cardinal;
PUNICODE_STRING = ^UNICODE_STRING;
UNICODE_STRING = packed record
Length: Word;
MaximumLength: Word;
Buffer: PWideChar;
end;
const
STATUS_ACCESS_DENIED = NTSTATUS($C0000022);
var
Old_LdrLoadDll: function(szcwPath: PWideChar; dwFlags: DWORD;
pUniModuleName: PUNICODE_STRING; pResultInstance: PPointer)
: NTSTATUS; stdcall;
function LdrLoadDll(szcwPath: PWideChar; dwFlags: DWORD;
pUniModuleName: PUNICODE_STRING; pResultInstance: PPointer)
: NTSTATUS; stdcall;
begin
Result := Old_LdrLoadDll(szcwPath, dwFlags, pUniModuleName, pResultInstance);
end;
procedure PatchCode(Address: Pointer; const NewCode; Size: Integer);
var
OldProtect: DWORD;
begin
if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then
begin
Move(NewCode, Address^, Size);
FlushInstructionCache(GetCurrentProcess, Address, Size);
VirtualProtect(Address, Size, OldProtect, @OldProtect);
end;
end;
type
PInstruction = ^TInstruction;
TInstruction = packed record
Opcode: Byte;
Offset: Integer;
end;
procedure RedirectProcedure(OldAddress, NewAddress: Pointer);
var
NewCode: TInstruction;
begin
NewCode.Opcode := $E9;
NewCode.Offset := NativeInt(NewAddress) - NativeInt(OldAddress) -
SizeOf(NewCode);
PatchCode(OldAddress, NewCode, SizeOf(NewCode));
end;
function NewLdrLoadDll(szcwPath: PWideChar; dwFlags: DWORD;
pUniModuleName: PUNICODE_STRING; pResultInstance: PPointer)
: NTSTATUS; stdcall;
begin
if (pos('111', pUniModuleName.Buffer) > 0) or
(pos('222', pUniModuleName.Buffer) > 0) then
Result := STATUS_ACCESS_DENIED
else
Result := LdrLoadDll(szcwPath, dwFlags, pUniModuleName, pResultInstance);
end;
begin
@Old_LdrLoadDll := GetProcAddress(GetModuleHandle('ntdll.dll'), 'LdrLoadDll');
try
RedirectProcedure(GetProcAddress(GetModuleHandle('ntdll.dll'),
'LdrLoadDll'), @NewLdrLoadDll);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
【问题讨论】:
-
您通过在原始过程的开头放置一个跳转来挂钩它。这也意味着,当您调用该原始过程时,您将再次遇到该跳转,再次跳转到您的新过程,并且基本上在某个点由于递归太深而导致堆栈溢出。
-
我也做过类似的事情(但对于 Delphi 函数,而不是库函数,尽管可能没有区别)。我的代码是in this forum post,用荷兰语做了一些解释(不过,代码中的 cmets 是英文的)。无论如何,这很简单。一个单元包含用于挂钩和取消挂钩函数的通用代码,另一个单元包含一些示例用法。
-
这个例子甚至展示了如何恢复原始文件并从你的钩子代码中调用它,所以我认为这正是你想要的。另一个显着的区别是我使用了 CALL ($E8) 而不是 JMP。我现在没有更多时间,但如果它成功了,请告诉我,我可能会在稍后使用内联代码将这些 cmets 扩展为完整答案。
-
@GolezTrol,我看到了你的代码,似乎你删除了调用原始函数的钩子。如何在不移除钩子的情况下调用原始函数(类似于我尝试将其保存到变量)?
-
问题是,你基本上通过把你的跳转放在那里破坏了原始函数的代码。您实际上覆盖了该函数的第一部分。因此,您已经存储了指向原始函数位置的指针,但这并没有给您原始代码。我很确定您必须删除钩子(即恢复原始代码)才能正确调用原始函数。但是给定的课程让这件事变得微不足道。如果您需要同时从多个线程执行此操作,这可能是一个问题......
标签: delphi delphi-10-seattle ntdll api-hook