我想寫一個小程式,用來將某軟體中一個不斷變化的數字,顯示在自己的小程式中,例如放大顯示在自己程式的Label中。
經過分析,考慮使用Hook 鉤住函式ExtTextOutW,然后將其內容回傳到主程式中顯示。
我現在已經能將目標軟體的ExtTextOutW函式 Hook住,但是不知道怎么才能使用回呼函式 將這個數字回傳給自己的程式。
請高手指點。
如果大家不嫌煩,我把代碼帖下面(Delphi7):
---------------------------------------------------------------------------------------
DLL主檔案:
library DLL_Hook;
uses
SysUtils,Windows,Classes,
APIHook in 'APIHook.pas',
ApiDefine in 'ApiDefine.pas';
{$R *.res}
var
HookHandle: HHook;
function HookProc(code:Integer;wparam:WPARAM;lparam:LPARAM):LRESULT;stdcall;
begin
Result := CallNextHookEx(HookHandle,code,wparam,lparam);
end;
function StartHook(SWindow: LongWORD ):BOOL;stdcall;
var
ThreadID: LongWORD;
begin
ThreadID := GetWindowThreadProcessId(sWindow, nil);
HookHandle := SetWindowsHookEx(WH_GETMESSAGE,@HookProc,HInstance,ThreadID);
if HookHandle <> 0 then Result := true
else Result := false;
end;
procedure StopHook;stdcall;
begin
UnhookWindowsHookEx(HookHandle);
end;
exports
StartHook name 'StartHook',
StopHook name 'StopHook';
begin
API_Hook;
end.
unit ApiDefine;
interface
uses
Windows, SysUtils, Classes,Messages,APIHook,ShellAPI;
procedure API_Hook;
procedure API_UnHook;
type
TExtTextOutW = function(DC:HDC;X:Integer;Y:Integer;options:Integer;
rect: PRect; Str: Pwidechar; Count: Longint; Dx: PInteger): BOOL; stdcall;
implementation
var
oldExtTextOutW : TExtTextOutW;
function NewExtTextOutW(DC:HDC;X:Integer;Y:Integer;options:Integer;
rect: PRect; Str: Pwidechar; Count: Longint; Dx: PInteger): BOOL; stdcall;
begin
//注釋下面這個陳述句之后,計算器上的文字無法顯示
//這說明,我們的Hook起作用了
//Result := oldExtTextOutW(DC,X,Y,options,rect,Str,count,dx);
end;
procedure API_Hook;
begin
if (@oldExtTextOutW = nil) then
@oldExtTextOutW := GetFunTrueAddress(@ExtTextOutW);
ReplaceFunAddress(@oldExtTextOutW,@NewExtTextOutW);
end;
procedure API_UnHook;
begin
if @oldExtTextOutW <> nil then
ReplaceFunAddress(@NewExtTextOutW,@oldExtTextOutW);
end;
initialization
finalization
API_UnHook;
end.
unit APIHook;
interface
uses
Windows, SysUtils, Classes;
type
//引入表入口資料結構
Image_Import_Entry = packed record
OriginalFirstThunk:DWORD;
TimeDateStamp:DWORD;
ForwarderChain:DWORD;
Name:DWORD;
FirstThunk:DWORD;
end;
PImage_Import_Entry = ^Image_Import_Entry;
TImportCode = packed record
JmpCode: Word;
AddressOfPFun: PPointer;
end;
PImportCode = ^TImportCode;
function GetFunTrueAddress(Code:Pointer):Pointer;
function ReplaceFunAddress(oldfun:Pointer;newfun:Pointer):Integer;
implementation
//獲得實際地址
function GetFunTrueAddress(Code: Pointer): Pointer;
var
func: PImportCode;
begin
Result := Code;
if Code = nil then exit;
try
func := code;
if (func.JmpCode = $25FF) then
begin
Result := func.AddressOfPFun^;
end;
except
Result := nil;
end;
end;
//替換地址
function ReplaceFunAddress(oldfun:Pointer;newfun:Pointer): Integer;
var
IsDone: TList;
function ReplaceAddressInModule(hModule: THandle; OldFunc, NewFunc: Pointer): Integer;
var
DosHeader: PImageDosHeader;
NTHeader: PImageNTHeaders;
ImportDesc: PImage_Import_Entry;
RVA: DWORD;
Func: ^Pointer;
DLL: string;
f: Pointer;
written: DWORD;
begin
Result := 0;
DosHeader := Pointer(hModule);
//已經找過,則退出
if IsDone.IndexOf(DosHeader) >= 0 then exit;
IsDone.Add(DosHeader);
oldfun := GetFunTrueAddress(OldFunc);
if IsBadReadPtr(DosHeader, SizeOf(TImageDosHeader)) then exit;
if DosHeader.e_magic <> IMAGE_DOS_SIGNATURE then exit;
NTHeader := Pointer(Integer(DosHeader) + DosHeader._lfanew);
//引入表的虛擬地址
RVA := NTHeader^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
if RVA = 0 then exit;
ImportDesc := pointer(integer(DosHeader) + RVA);
while (ImportDesc^.Name <> 0) do
begin
//引入檔案名
DLL := PChar(Integer(DosHeader) + ImportDesc^.Name);
//獲得該DLL的句柄,然后遞回查找
ReplaceAddressInModule(GetModuleHandle(PChar(DLL)), oldfun, newfun);
//引入函式入口
Func := Pointer(Integer(DOSHeader) + ImportDesc.FirstThunk);
//如果函式指標不為空
while Func^ <> nil do
begin
//取得真是地址
f := GetFunTrueAddress(Func^);
//如果和我們要攔截的Api函式地址一樣
if f = oldfun then
begin
//替換成我們自己的Api地址
WriteProcessMemory(GetCurrentProcess, Func, @NewFunc, 4, written);
if Written > 0 then Inc(Result);
end;
//繼續找
Inc(Func);
end;
Inc(ImportDesc);
end;
end;
begin
IsDone := TList.Create;
try
//GetModuleHandle,引數nil,為獲取自身的模塊句柄
Result := ReplaceAddressInModule(GetModuleHandle(nil), oldfun, newfun);
finally
IsDone.Free;
end;
end;
end.
用于測驗的程式:
unit TestHook;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TCallBackProc = procedure(msg:string);stdcall;
TForm1 = class(TForm)
btnUnHook: TButton;
Edit1: TEdit;
btnStartHook: TButton;
Label1: TLabel;
procedure btnUnHookClick(Sender: TObject);
procedure btnStartHookClick(Sender: TObject);
private
{ Private declarations }
function GetTargetWin(title:string):HWnd;
public
{ Public declarations }
end;
var
Form1: TForm1;
StartHook : function(SWindow:LongWORD):boolean;stdcall;
StopHook : procedure;stdcall;
implementation
{$R *.dfm}
procedure TForm1.btnUnHookClick(Sender: TObject);
begin
Caption := '';
if Assigned(@StopHook) then StopHook;
end;
procedure TForm1.btnStartHookClick(Sender: TObject);
var
ModuleHandle: THandle;
TmpWnd: HWnd;
begin
TmpWnd := GetTargetWin(Edit1.Text );
if not isWindow(TmpWnd) then
begin
MessageBox(self.Handle, '沒有找到視窗', 'Warning', MB_OK);
exit;
end;
ModuleHandle := LoadLibrary('DLL_Hook.dll');
@StartHook := GetProcAddress(ModuleHandle, 'StartHook');
@StopHook := GetProcAddress(ModuleHandle, 'StopHook');
if Assigned(@StartHook) then
begin
if StartHook(TmpWnd) then
begin
Caption := 'Hook成功!';
end
else MessageBox(self.Handle, 'Hook失敗!', 'Error', MB_OK);
end;
end;
//找到指定視窗
function TForm1.GetTargetWin(title:string):HWnd;
var
h: HWnd;
p: array[0..254] of char;
s: string;
begin
result := 0;
h := GetWindow(Handle, GW_HWNDFIRST);
while h <> 0 do
begin
if GetWindowText(h, p, 255) > 0 then
begin
s := PChar(@p);
//s := LeftStr(s,2);
if length(s) < length(title) then begin
h := GetWindow(h, GW_HWNDNEXT);
continue;
end;
s := copy(s,1,length(title));
if s = title then
begin
Result := h;
break;
end;
end;
h := GetWindow(h, GW_HWNDNEXT);
end;
end;
end.
uj5u.com熱心網友回復:
回傳給自己程式的方法可以是訊息,PostMessage到自己程式的主表單,也可以是管道,還可以是socket,還有共享記憶體,你注入別人行程后,實際上和你自己的主行程是2個行程,具體技術要點,你可以搜索"windows 行程間通信",祝順利解決問題。uj5u.com熱心網友回復:
PostMessage(。。)傳遞結構體指標uj5u.com熱心網友回復:
如果主副程式都是自己撰寫,可以主程式向副程式,發送資訊來完成。uj5u.com熱心網友回復:
另外主副程式也可以 通過共享記憶體方式,獲取資料。uj5u.com熱心網友回復:
各種辦法,共享記憶體,WM_COPYDATA訊息,管道,郵槽.......轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/88813.html
