不怕死机的壮士请进!!(100分)

  • 主题发起人 主题发起人 yygw
  • 开始时间 开始时间
Y

yygw

Unregistered / Unconfirmed
GUEST, unregistred user!
我在做写一个用于98下的32位鼠标取词程序,其中挂接ExtTextOutA的操作用以下代码完成。
可是在测试时,如果鼠标指向快速启动栏或最小化窗口时死机,用SoftICE也无法截获这种错误。
代码如下,谁能帮我把它调试好。

数据定义单元
unit DefUnit;

interface

uses
Messages, Windows;

const
WM_GETWORD = WM_USER + Ord('Y') + Ord('y') + Ord('g') + Ord('w');
csMappingFileName = 'Mapping File By Yygw'; //内存映象名
csMaxStringLen = 64; //最大字符串长度
csCodeSize = 4096; //加载到内存映象视图的代码长度
csJmpCode = $E9; //跳转指令机器码

csGdiName = 'GDI32'; //GDI32.DLL
csApiFunName = 'ExtTextOutA'; //函数名
csUserName = 'HOOK_DLL'; //用户DLL名
csUserFunName = 'MyExtTextOutA'; //用户函数名

type
TIDT = array [0..5] of Byte; //IDT表

TLongJump = packed record //跳转指令
JmpOp: Byte; //操作码
Addr: Pointer; //地址
end;

PShareMem = ^TShareMem;
TShareMem = packed record
//以下参数面向主程序
hProcWnd: HWND; //应用程序主窗口
hHookWnd: HWND; //当前挂钩窗口
hProc: THandle; //应用程序进程ID
pMouse: TPoint; //鼠标位置
HookDelay: Cardinal; //取词延时(ms)
Enabled: Boolean; //允许取词
lpText: array[0..csMaxStringLen] of Char; //当前取到的词
TextRect: TRect; //当前文本框

//以下参数内部使用
lpOldExtTextOutA: Pointer; //旧函数入口
lpNewExtTextOutA: Pointer; //自定义函数入口
lpRing0_WriteMemory: Pointer; //挂接函数入口
lpOldRing0_WriteMemory: Pointer; //旧代码入口

AllowTake: Boolean; //可以取词
OldCode: TLongJump; //旧函数代码
NewCode: TLongJump; //跳转代码
lpSour, lpDes: Pointer; //内存复制地址

lpOldGate: DWord; //旧中断门入口
IDT: TIDT; //IDT数据

Code: array [0..csCodeSize - 1] of Byte; //代码区
end;

const
SizeOfLongJump = SizeOf(TLongJump);

var
pShMem: pShareMem; //指向一块>2G的共享地址

implementation

end.

//Api挂接单元,需调试
unit ApiHook;

interface

uses
Messages, Windows;

function InitApiHook: Boolean; //初始化挂接
function InstallApi: Boolean; //挂接ExtTextOutA
function UnInstallApi: Boolean; //卸去挂接
function CloseApiHook: Boolean; //关闭挂接

implementation

uses
DefUnit;

var
pHookMem: PShareMem; //伪指针,实际代码中被替换为pShMem
hMappingFile: THandle; //映象文件句柄
Inited: Boolean; //已初始化
Hooked: Boolean; //已挂接

const
csExceptionUsed = 5; //中断号

//跳到Ring0修改内存的过程
//源地址和目标地址放在pShMem^.lpSour和lpDes中
//实际代码被加载到pShMem^.Code中可由所有进程共享调用运行
//内部的pHookMem被替换为pShMem的值
procedure Ring0_WriteMemory;
asm
PUSHA //保存现场
LEA EAX, pHookMem
SIDT TShareMem([EAX]).IDT //获取 IDT

MOV EBX, DWORD PTR [TShareMem([EAX]).IDT + 2]
ADD EBX, 8 * csExceptionUsed //Ebx -> IDT 中 csExceptionUsed 中断入口

CLI //清中断

MOV DX, WORD PTR [EBX + 6] //保存中断门高字
SHL EDX, 16
MOV DX, WORD PTR [EBX] //低字
MOV DWORD PTR [TShareMem([EAX]).lpOldGate], EDX

//计算实际运行的代码和原代码的地址差
MOV ECX, DWORD PTR [TShareMem([EAX]).lpRing0_WriteMemory]
SUB ECX, DWORD PTR [TShareMem([EAX]).lpOldRing0_WriteMemory]
LEA EAX, @Ring0Code //"安装挂钩" - 用户中断门
ADD EAX, ECX //获得运行代码的@Ring0Code地址

MOV WORD PTR [EBX], AX //低字
SHR EAX, 16
MOV WORD PTR [EBX + 6], AX //高字

DB $CD, csExceptionUsed //INT csExceptionUsed 的机器码
//触发中断跳到Ring0代码

LEA EAX, pHookMem
MOV EBX, DWORD PTR [TShareMem([EAX]).IDT + 2] //恢复中断门
ADD EBX, 8 * csExceptionUsed
MOV EDX, DWORD PTR [TShareMem([EAX]).lpOldGate]
MOV WORD PTR [EBX], DX
SHR EDX, 16
MOV WORD PTR [EBX +6], DX
JMP @Exit

@Ring0Code: //运行在Ring0下的代码
CLI
LEA EAX, pHookMem
MOV EBX, TShareMem[EAX].lpSour //源地址
MOV EDX, TShareMem[EAX].lpDes //目标地址
MOV ECX, SizeOfLongJump

@Loop:
MOV AL, [EBX] //内存复制
MOV [EDX], AL
INC EBX
INC EDX
LOOP @Loop
IRETD //返回到Ring3

@Exit:
POPA //恢复现场
end;

//用户的ExtTextOutA函数
//实际代码被加载到pShMem^.Code中可由所有进程共享调用运行
//内部的pHookMem被替换为pShMem的值
procedure MyExtTextOutA;
asm
PUSH EBP
MOV EBP, ESP

//以下代码用SoftICE跟踪调试时正常
//但实际使用时在窗口最小化或鼠标指向快速启动栏时死机
{PUSH EBX
PUSH ECX
PUSH EDX
PUSH ESI
PUSH EDI

//恢复原ExtTextOutA代码
LEA EAX, pHookMem
LEA EBX, TShareMem([EAX]).OldCode //旧函数代码
MOV TShareMem([EAX]).lpSour, EBX
MOV EBX, TShareMem([EAX]).lpOldExtTextOutA //旧函数入口
MOV TShareMem([EAX]).lpDes, EBX
MOV EBX, TShareMem([EAX]).lpRing0_WriteMemory
CALL EBX //调用Ring0_WriteMemory恢复原函数代码

//调用原ExtTextOutA
MOV EAX, [EBP + $24] //lpDx
PUSH EAX
MOV EAX, [EBP + $20] //cbCount
PUSH EAX
MOV EAX, [EBP + $1C] //lpString
PUSH EAX
MOV EAX, [EBP + $18] //lprc
PUSH EAX
MOV EAX, [EBP + $14] //fuOptions
PUSH EAX
MOV EAX, [EBP + $10] //Y
PUSH EAX
MOV EAX, [EBP + $0C] //X
PUSH EAX
MOV EAX, [EBP + $08] //hdc
PUSH EAX
LEA EAX, pHookMem
MOV EBX, TShareMem([EAX]).lpOldExtTextOutA
CALL EBX
PUSH EAX //返回结果

//重新挂接ExtTextOutA
LEA EAX, pHookMem
LEA EBX, TShareMem([EAX]).NewCode //跳转指令
MOV TShareMem([EAX]).lpSour, EBX
MOV EBX, TShareMem([EAX]).lpOldExtTextOutA //原函数入口
MOV TShareMem([EAX]).lpDes, EBX
MOV EBX, TShareMem([EAX]).lpRing0_WriteMemory
CALL EBX //调用Ring0_WriteMemory重新挂接

POP EAX

POP EDI
POP ESI
POP EDX
POP ECX
POP EBX}

MOV EAX, False //调试时用,返回假

@Exit:
POP EBP //返回
RET $20
end;

//安装Api挂钩
function InstallApi: Boolean;
var
pHookCode: Pointer; //要加载到pShMem^.Code中的代码入口
CodeSize: Integer; //实际代码长度
pInt: PInteger; //指向一个32位整数
i: Integer;
Ring0Fun: Pointer; //实际的Ring0_WriteMemory入口
begin
Result := False;
if Hooked or not Inited then
Exit; //已挂接过或未初始化
Hooked := False;
pShMem^.lpOldExtTextOutA := GetProcAddress(GetModuleHandle(csGdiName),
csApiFunName); //原函数入口
if pShMem^.lpOldExtTextOutA <> nil then
begin
//pShMem^.lpOldExtTextOutA := Pointer($BFF21CB8);
//我的机器上有别的挂钩程序,得不到ExtTextOutA的正确地址,只好手动修改
pHookCode := @Ring0_WriteMemory; //要加载到pShMem^.Code的代码入口
CodeSize := Cardinal(@InstallApi) - Cardinal(@Ring0_WriteMemory);
CopyMemory(@pShMem^.Code, pHookCode, CodeSize); //复制代码
for i := 0 to CodeSize - 1 do
if Cardinal((@pShMem^.Code)^) = Cardinal(@pHookMem) then
begin
pInt := @pShMem^.Code; //查找代码中的pHookMem并将它替换为pShMem
pInt^ := Cardinal(pShMem);
end;
pShMem^.lpRing0_WriteMemory := @pShMem^.Code; //Ring0_WriteMemory入口
pShMem^.lpOldRing0_WriteMemory := @Ring0_WriteMemory; //原始代码入口
pShMem^.lpNewExtTextOutA := Pointer(Cardinal(@pShMem^.Code) +
Cardinal(@MyExtTextOutA) - Cardinal(@Ring0_WriteMemory)); //用户ExtTextOutA入口
CopyMemory(@pShMem^.OldCode, pShMem^.lpOldExtTextOutA, SizeOf(TLongJump)); //保存原函数代码
pShMem^.NewCode.JmpOp := csJmpCode; //产生跳转代码
pShMem^.NewCode.Addr := Pointer(Cardinal(pShMem^.lpNewExtTextOutA) -
Cardinal(pShMem^.lpOldExtTextOutA) - SizeOf(TLongJump));
pShMem^.lpSour := @pShMem^.NewCode; //新跳转代码
pShMem^.lpDes := pShMem^.lpOldExtTextOutA; //原函数入口
Ring0Fun := pShMem^.lpRing0_WriteMemory; //Ring0_WriteMemory入口
asm
MOV EAX, Ring0Fun //挂接MyExtTextOutA
CALL EAX
end;
Result := True;
Hooked := True;
end;
end;

//释放Api挂钩
function UnInstallApi: Boolean;
var
Ring0Fun: Pointer; //实际的Ring0_WriteMemory入口
begin
if not Hooked then
begin
Result := False;
Exit;
end;
pShMem^.Enabled := False;
pShMem^.AllowTake := False;
pShMem^.lpSour := @pShMem^.OldCode; //原函数代码
pShMem^.lpDes := pShMem^.lpOldExtTextOutA; //原函数入口
Ring0Fun := pShMem^.lpRing0_WriteMemory; //Ring0_WriteMemory入口
asm
MOV EAX, Ring0Fun //恢复原函数代码
CALL EAX
end;
Hooked := False;
Result := True;
end;

//创建内存映象文件,初始化Api挂接环境
function InitApiHook: Boolean;
begin
Inited := False;
Hooked := False;
hMappingFile := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,
SizeOf(TShareMem), PChar(csMappingFileName)); //文件映象
if hMappingFile <> 0 then
begin
pShMem := PShareMem(MapViewOfFile(hMappingFile, FILE_MAP_WRITE, 0, 0, 0));
if pShMem <> nil then //映射视图,得到一块地址>2G被所有进程共享的空间
begin
ZeroMemory(pShMem, SizeOf(TShareMem)); //数据块清零
Inited := True;
end else begin
CloseHandle(hMappingFile); //无法创建内存块
MessageBox(GetCurrentProcess, 'Cannot create the Share Memory Block!'
, 'Error', MB_OK)
end;
end
else
MessageBox(GetCurrentProcess, 'Cannot create the Share Memory Block!'
, 'Error', MB_OK); //无法创建文件映象
Result := Inited;
end;

//关闭映象文件,释放共享内存块
function CloseApiHook: Boolean;
begin
if not Inited then
begin
Result := False;
Exit;
end;
UnInstallApi; //取消挂接
UnMapViewOfFile(pShMem); //释放内存块
CloseHandle(hMappingFile); //关闭句柄
Inited := False;
Result := True;
end;

end.

测试用主窗口单元
unit MainFrm;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

type
TMainForm = class(TForm)
cbHook: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure cbHookClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
MainForm: TMainForm;

implementation

uses ApiHook;

{$R *.DFM}

procedure TMainForm.FormCreate(Sender: TObject);
begin
InitApiHook;
InstallApi;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
CloseApiHook;
end;

procedure TMainForm.cbHookClick(Sender: TObject);
begin
if cbHook.Checked then
InstallApi
else
UnInstallApi;
end;

end.
 
我试试
不过你也做的太复杂了
没有必要
 
我的天。。。汇编???
 
不怕死机的壮士请进!!
想骗分的壮士也请进!!!
 
!·#¥%…
我走先
 
接受答案了.
 
请问yygw,此问题解决了吗?请把解决死机问题的代码发给我一份,
不盛感激。ningkui@263.net
 
我也想要一份,aheken@263.net
thx
 
yygw,你的程序我无法编译运行,不过我根据你的解题思路,用VC建立一个DLL,方法和你基本一样,刚开始和你说的情况一样,后来修改了汇编代码后解决该问题,不过又有新情况,和word不能兼容,希望和你共同探讨,ruh◎21cn.com
 
后退
顶部