能力值:
( LV3,RANK:20 )
|
-
-
2 楼
用XT可以看到NTReadVirtualMemory NTWRiteVirtualMemory NTAllocateVirtualMemory 被inline hook了,以下为函数源代码。也就是需要恢复的代码。
NTReadVirtualMemory 0x805B52C2
push 1C
push 804DAEF0
call 8053CBE0
mov eax, dword ptr fs:[124]
mov edi, eax
mov al, byte ptr [edi+140]
mov byte ptr [ebp-20], al
mov esi, dword ptr [ebp+14]
test al, al
je short 805B534C 应跳转至0X805B52E4继续执行 HOOK 32字节NTReadVirtualMemory +0x22
--------------------
NTReadVirtualMemory HOOK后
jmp 88E52C20
dec ebp
sub al, 12
jns short 805B52C5
jmp dword ptr [ecx+24]
add dword ptr [eax], eax
add byte ptr [ebx+40878AF8], cl
add dword ptr [eax], eax
add byte ptr [eax+758BE045], cl
adc al, 84
Invalid Command
je short 805B534C
-------------------------
NTWRiteVirtualMemory 0x805B53CC
push 1C
push 804DAF08
call 8053CBE0
mov eax, dword ptr fs:[124]
mov edi, eax
mov al, byte ptr [edi+140]
mov byte ptr [ebp-20], al
mov esi, dword ptr [ebp+14]
test al, al
je short 80565456应跳转到0x805B53EE继续执行 NTWRiteVirtualMemory+0x22
-------------------
NTWRiteVirtualMemory HOOK后
jmp 88E52FB0
dec ebp
sub al, 8
js short 805B53CF
jmp dword ptr [ecx+24]
add dword ptr [eax], eax
add byte ptr [ebx+40878AF8], cl
add dword ptr [eax], eax
add byte ptr [eax+758BE045], cl
adc al, 84
Invalid Command
je short 805B5456
---------------------------------
NTAllocateVirtualMemory 0x805A9ABA
push 118
push 804DACB8 应跳转到0x805A9ABF hook5字节 NTAllocateVirtualMemory +5
----------------
NTAllocateVirtualMemory HOOK 后
jmp 88E53070
push 804DACB8
|
能力值:
( LV3,RANK:20 )
|
-
-
3 楼
首先感谢论坛的crazyearl 本文代码根据他发表的过HS保护中的源码 分析编写注释 十分感谢为我们新手提供那么好的文章!
本篇文章适用于菜鸟级新手查看,分析。
该代码是我学习过程中一个实践,关于写驱动框架结构,驱动层inline hook,分析,绕过游戏驱动保护HOOK函数方法。
也是学习郁金香驱动保护部分课程的一个实践。
希望给像我一样零编程基础学驱动的菜鸟们一些分享,帮助。 期待喜欢这方面的朋友交流 ^_^ 与君共勉!
前篇写了通过KD XT工具过日服巫术驱动保护 使OD CE 正常加载教程,上篇写了被驱动保护HOOK的函数具体分析,找到恢复位置,这篇写下如何写代码恢复被HOOK的函数。
思路: 找到被HOOK函数的地址,然后HOOK它前5个字节,写个jmp 自写函数 让它按照函数正常流程运行,跳过游戏检测call。
首先要做的仍然是用工具 挂起检测线程,干掉系统回调。然后加载我们自己的驱动。
加载驱动软件:DriverMonitor 调试信息查看:DebugView
注意!!(本驱动在xp 32位win7 32位ddk编译通过。xp32位"系统下载吧GhostXP SP3 2011装机版V8.0"测试通过,因汇编部分有些硬编码,不同系统可能会有所不同,请加载驱动前先用Kernel_Detective或xuetr查看相关代码,适当修改,否则蓝屏是跑不了的啦 ^_^)
驱动代码中注释掉一些关于进程比较的代码,思路是如果游戏访问关键函数,让它执行自己的检测代码,其他执行我们HOOK的代码。
具体实现代码以后在补充。
驱动代码见下文:
/********************************************************************
* 创建时间: 2012/03/16
* 文件名称: ByPass.c
* 文件作者: 月夜翔龙* ===================================================================
* 功能说明: 入口文件
* -------------------------------------------------------------------
* 其他说明:
*********************************************************************/
#include <ntddk.h>
#include "ddk_proc.h"
#include "GetAddrs.h"
#define INITCODE code_seg("INIT")
#define PAGECODE code_seg("PAGE")
#pragma INITCODE
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING B)
{
hookWriteMemory(); //调用自写hook函数,恢复系统原函数,跳过游戏检测CALL
HookReadVirtualMemory();
HookAllocateVirtualMemory();
//注册派遣例程
pDriverObject->MajorFunction[IRP_MJ_CREATE]=ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数
pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数
pDriverObject->MajorFunction[IRP_MJ_READ]=ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数
pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数
CreatMyDevice(pDriverObject); //创建驱动设备
pDriverObject->DriverUnload=ddk_unload; //驱动卸载例程
//return (1);
return STATUS_SUCCESS;
}
#pragma PAGECODE
NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp )
{
//对相应的IPR进行处理
pIrp->IoStatus.Information=0;//设置操作的字节数为0,这里无实际意义
pIrp->IoStatus.Status=STATUS_SUCCESS;//返回成功
IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP
KdPrint(("离开派遣函数\n"));//调试信息
return STATUS_SUCCESS; //返回成功
}
void ddk_unload(IN PDRIVER_OBJECT pDriverObject)
{
UNICODE_STRING symboName;
PDEVICE_OBJECT pdev; //用来卸载设备
uninstall(); //恢复HOOK
RtlInitUnicodeString(&symboName,L"\\??\\ByPass_symlink");
pdev=pDriverObject->DeviceObject;
IoDeleteSymbolicLink(&symboName);//删除符号链接
IoDeleteDevice(pdev); //删除设备对象
KdPrint(("驱动卸载成功。。。。。。OK\n\n"));
}
---------------------------------------------------------------------------------------
/********************************************************************
* 创建时间: 2012/03/16
* 文件名称: ddk_proc.h 头文件
* 文件作者: 月夜翔龙
* ===================================================================
* 功能说明: 变量定义,函数声明部分
* -------------------------------------------------------------------
* 其他说明:
*********************************************************************/
#include <windef.h>
#include <ntddk.h>
/************************************************
*** 函数前置声明部分 ******
************************************************/
NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp ); //派遣函数
extern long KeServiceDescriptorTable ; //导出SSDT表
void ddk_unload (IN PDRIVER_OBJECT pDriverObject); //前置说明 卸载历程
void wpoff(); //关闭页面保护 具体实现过程在下面
void wpon(); //开启页面保护 具体实现过程在下面
NTKERNELAPI UCHAR * PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(__in HANDLE ProcessId,__out PEPROCESS *Process);
/**************************************************
********** 全局变量 *****************
**************************************************/
ULONG WriteMemoryAddr,writeMemory_22,writeMemory_7; //HOOK writeMemory使用
ULONG ReadMemoryAddr,readMemory_22; //HOOK readmemory 使用
ULONG AllocateMemoryAddr,AllocateMemory_5; //HOOK AllocateMemory使用
#define NAKED __declspec(naked) //定义裸体函数
//*******************************************************************
//PEPROCESS WriteMemoryEPROCESS = NULL; //进程比较使用 该游戏不需要
// ULONG WriteMemoryEPROCESS_174; //
//ANSI_STRING wm_str1,wm_str2; //保存进程名称
//#define APPNAME "WizardryOnline"
/**********************************************************
*** 函数实现部分 ***
**********************************************************/
//////////////////////////////////////////////////////////////////////
// 名称: CreatMyDevice
// 功能: 创建设备
// 参数: 驱动对象 在入口函数传递
// 返回: status
//////////////////////////////////////////////////////////////////////
NTSTATUS CreatMyDevice(IN PDRIVER_OBJECT pDriverObject)
{
NTSTATUS status;
PDEVICE_OBJECT pDevObj; //用来返回设备
UNICODE_STRING devName; //设备名称
UNICODE_STRING symlinkname;
RtlInitUnicodeString(&devName,L"\\Device\\ByPass_Devcice"); //初始化设备名称
//创建设备
status=IoCreateDevice(pDriverObject,0,&devName,FILE_DEVICE_UNKNOWN,0,TRUE,&pDevObj);
if(!NT_SUCCESS(status)) //如果创建设备失败
{
if (status==STATUS_INSUFFICIENT_RESOURCES)
{
KdPrint(("驱动资源不足.....++++++++++++"));
}
if (status==STATUS_OBJECT_NAME_EXISTS)
{
KdPrint(("驱动对象名已存在.....++++++++++++"));
}
if (status==STATUS_OBJECT_NAME_COLLISION)
{
KdPrint(("驱动对象名有冲突....++++++++++++"));
}
KdPrint(("设备创建失败..........+++++++"));
return status;
}
pDevObj->Flags |= DO_BUFFERED_IO ;
//创建符号链接
RtlInitUnicodeString(&symlinkname,L"\\??\\ByPass_symlink");
status=IoCreateSymbolicLink(&symlinkname,&devName);
if (!NT_SUCCESS(status))
{
IoDeleteDevice(pDevObj); //如果创建设备符号链接不成功则删除
return status;
}
KdPrint(("设备创建成功...............++\n"));
return STATUS_SUCCESS;
}
//////////////////////////////////////////////////////////////////////
// 名称: WPOFF
// 功能: 关闭页面保护
// 参数:
// 返回:
//////////////////////////////////////////////////////////////////////
void wpoff()
{
__asm
{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
}
//////////////////////////////////////////////////////////////////////
// 名称: WPON
// 功能: 开启页面保护
// 参数:
// 返回:
//////////////////////////////////////////////////////////////////////
void wpon()
{
__asm
{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
//////////////////////////////////////////////////////////////////////
// 名称: 卸载函数
// 功能: 处理卸载驱动要做的事情,恢复自写HOOK
// 参数:
// 返回:
//////////////////////////////////////////////////////////////////////
void uninstall()
{
KIRQL Irql;
//WriteMemoryAddr=CurNTWriteVirtualMemory();
BYTE byWriteVirtualMemory[7] = {0x6A,0x1C,0x68,0x08,0xAF,0x4D,0x80}; //系统原WriteVirtualMemory前7字节 硬编码 请适当修改
BYTE byReadVirtualMemory[7] = {0x6A,0x1C,0x68,0xF0,0xAE,0x4D,0x80}; //系统原ReadVirtualMemory前7字节 硬编码 请适当修改
BYTE byAllocateVirtualMemory[5] = {0x68,0x18,0x01,0,0}; //系统原AllocateVirtualMemory前5字节 硬编码 请适当修改
wpoff(); //关闭页面保护
Irql=KeRaiseIrqlToDpcLevel(); //提升IRQ等级
////恢复WriteVirtualMemory
RtlCopyMemory((BYTE*)WriteMemoryAddr,byWriteVirtualMemory,7);
////恢复ReadVirtualMemory
RtlCopyMemory((BYTE*)ReadMemoryAddr,byReadVirtualMemory,7);
////恢复AllocateVirtualMemory
RtlCopyMemory((BYTE*)AllocateMemoryAddr,byAllocateVirtualMemory,5);
//恢复IRQ中断等级
KeLowerIrql(Irql);
//开启页面保护
wpon();
}
---------------------------------------------------------------------------------------------------------
/********************************************************************
* 创建时间: 2012/03/16
* 文件名称: GetAddrs.h 头文件
* 文件作者: 月夜翔龙
* ===================================================================
* 功能说明: 获得函数首地址,自写HOOK,跳过游戏检测CALL
* -------------------------------------------------------------------
* 其他说明:
*********************************************************************/
/*********************************************************
**********功能: 获得当前ReadVirtualMemory地址 ************
**********参数: 无 ***********************************
*********返回值: 函数地址 ********************************
*********公式: 函数地址=SSDT表基地址+函数标号*4 **********
*********************************************************/
ULONG CurNTReadVirtualMemory()
{
ULONG ReadMemoryBase;
__asm
{
push eax //存放ssdt地址
push ebx //存放函数在SSDT表索引号 ReadVirtualMemory 十进制186 十六进制0XBA 不同系统索引号可能不同 KD查看SSDT表
mov eax,KeServiceDescriptorTable
mov eax,[eax] //取出ssdt基地址
mov ebx,0xBA //存放索引号
shl ebx,2 //0xBA*4 shl ebx,2 == imul ebx,ebx,4
add eax,ebx //KeServiceDescriptorTable+0xBA*4
mov eax,[eax] //取出函数地址
mov ReadMemoryBase,eax //保存到ReadMemoryBase
pop eax
pop ebx
}
KdPrint(("curReadMemory值为%x +++++++\n",ReadMemoryBase));
return ReadMemoryBase;
}
/*********************************************************
**********功能: 获得当前WriteVirtualMemory地址 ************
**********参数: 无 ***********************************
*********返回值: 函数地址 ********************************
*********公式: 函数地址=SSDT表基地址+函数标号*4 **********
*********************************************************/
ULONG CurNTWriteVirtualMemory()
{
ULONG WriteMemoryBase;
__asm
{
push eax //存放ssdt地址
push ebx //存放函数在SSDT表索引号 WriteVirtualMemory 十进制277 十六进制0X115 不同系统索引号可能不同 KD查看SSDT表
mov eax,KeServiceDescriptorTable
mov eax,[eax] //取出ssdt基地址
mov ebx,0x115 //存放索引号
shl ebx,2 //0xBA*4 shl ebx,2 == imul ebx,ebx,4
add eax,ebx //KeServiceDescriptorTable+0x115*4
mov eax,[eax] //取出函数地址
mov WriteMemoryBase,eax //保存到WriteMemoryBase
pop eax
pop ebx
}
KdPrint(("curWriteMemory值为%x +++++++\n",WriteMemoryBase));
return WriteMemoryBase;
}
/*********************************************************
**********功能: 获得当前NTAllocateVirtualMemory地址 ******
**********参数: 无 ***********************************
*********返回值: 函数地址 ********************************
*********公式: 函数地址=SSDT表基地址+函数索引号*4 ********
*********************************************************/
ULONG CurNTAllocateVirtualMemory()
{
ULONG AllocateMemoryBase;
__asm
{
push eax //存放ssdt地址
push ebx //存放函数在SSDT表索引号 AllocateMemory 十进制17十六进制0X11 不同系统索引号可能不同 KD查看SSDT表
mov eax,KeServiceDescriptorTable
mov eax,[eax] //取出ssdt基地址
mov ebx,0x11 //存放索引号
shl ebx,2 //0x11*4 shl ebx,2 == imul ebx,ebx,4
add eax,ebx //KeServiceDescriptorTable+0x11*4
mov eax,[eax] //取出函数地址
mov AllocateMemoryBase,eax //保存到AllocateMemoryBase
pop eax
pop ebx
}
KdPrint(("curAllocateMemory值为%x +++++++\n",AllocateMemoryBase));
return AllocateMemoryBase;
}
/*********************************************************
**********功能: 通过MmGetSystemRoutineAddress()获取地址***
**************** 演示,功能同上 ******
**********参数: 无 ******
*********返回值: 函数地址 ******
*********公式: 无 ******
**********************************************************
ULONG OldNTAllocateVirtualMemory()
{
ULONG OldAllocateMemoryBase;
UNICODE_STRING S_AllocateMemory;
RtlInitUnicodeString(&S_AllocateMemory,L"NtAllocateVirtualMemory");//初始化字串
OldAllocateMemoryBase=(ULONG)MmGetSystemRoutineAddress(&S_AllocateMemory);//获得地址
KdPrint(("OldAllocateMemory值为%x +++\n",OldAllocateMemoryBase));
return OldAllocateMemoryBase;
}
*/
/*********************************************************
**********功能: 恢复WriteVirtualMemory正常流程 ******
**************** 跳过游戏检测CALL ******
**********参数: 无 ******
*********返回值: 无 ******
*********公式: 无 ******
*********************************************************/
static NAKED void MyWriteVirtualMemory()
{
/*进程比较代码实现,该游戏不需要
获得调用者的EPROCESS
WriteMemoryEPROCESS = IoGetCurrentProcess();
KdPrint(("WriteMemoryEPROCESS值为%x ++++\n",WriteMemoryEPROCESS));
WriteMemoryEPROCESS_174=(ULONG)WriteMemoryEPROCESS+0x174;
KdPrint(("WriteMemoryEPROCESS+0x174值为%x ++++\n",WriteMemoryEPROCESS_174));
将调用者的进程名保存到str1中
RtlInitAnsiString(&wm_str1,(PCSZ)WriteMemoryEPROCESS_174);
将我们要比对的进程名放入str2
RtlInitAnsiString(&wm_str2,APPNAME);
KdPrint(("当前进程名称值为:%x ++++\n",&wm_str1));
KdPrint(("APPNAME值为:%x ++++\n",&wm_str2));
if (RtlCompareString(&wm_str1,&wm_str2,FALSE) == 0)
*/
__asm
{
//0x805B53CC
push 0x1C
push 0x804DAF08 //硬编码,不同系统可能有所不同,请按实际修改
mov eax,0x8053CBE0 //硬编码,不同系统可能有所不同,请按实际修改
call eax
mov eax, dword ptr fs:[0x124]
mov edi, eax
mov al, byte ptr [edi+0x140]
mov byte ptr [ebp-0x20], al
mov esi, dword ptr [ebp+0x14]
test al, al
mov eax,writeMemory_22
jmp eax //恢复writememory前32字节然后跳转到writememory+0x22正常执行,跳过游戏检测call
} //end asm
}
/*********************************************************
**********功能: 恢复ReadVirtual Memory正常流程 ******
**************** 跳过游戏检测CALL ******
**********参数: 无 ******
*********返回值: 无 ******
*********公式: 无 ******
*********************************************************/
static NAKED void MyReadVirtualMemory()
{
__asm
{
//0x805B52C2
push 0x1C
push 0x804DAEF0 //硬编码,不同系统可能有所不同,请按实际修改
mov eax,0x8053CBE0 //硬编码,不同系统可能有所不同,请按实际修改
call eax
mov eax, dword ptr fs:[0x124]
mov edi, eax
mov al, byte ptr [edi+0x140]
mov byte ptr [ebp-0x20], al
mov esi, dword ptr [ebp+0x14]
test al, al
mov eax,readMemory_22 //恢复readmemory前32字节然后跳转到readmemory+0x22正常执行,跳过游戏检测call
jmp eax
}
}
/*********************************************************
**********功能: 恢复AllocateVirtualMemory正常流程 ******
**************** 跳过游戏检测CALL ******
**********参数: 无 ******
*********返回值: 无 ******
*********公式: 无 ******
*********************************************************/
static NAKED void MyAllocateVirtualMemory()
{
__asm
{
//0x805A9ABA
push 0x118 //硬编码,不同系统可能有所不同,请按实际修改
mov eax,AllocateMemory_5//恢复前5字节然后跳转到allocatememory+0x5正常执行,跳过游戏检测call
jmp eax
}
}
/*********************************************************
**********功能:hook原函数头5个字节跳转到自写函数执行******
**************** ******
**********参数: 无 ******
*********返回值: 无 ******
*********公式:jmp jmpaddr jmp机器码E9 ******
*********jmpaddr公式:新地址-(原地址+5)或-原地址-5 ******
*********************************************************/
void hookWriteMemory()
{
BYTE JmpAddress[7] = {0xE9,0,0,0,0,0x90,0x90};
//该数组首字节为E9,即是jmp指令,后四字节为跳转字节,故为0,
//0x90为NOP指令,因WriteMemory汇编代码头部是两条指令7字节 故后2字节用nop填充
KIRQL Irql;
WriteMemoryAddr=CurNTWriteVirtualMemory();//获得当前WriteVirtualMemory地址 即原地址
writeMemory_22=WriteMemoryAddr+0x22; //WriteVirtualMemory+0x22 恢复前32字节然后跳转到这里继续执行
*(ULONG *)(JmpAddress+1)=(ULONG)MyWriteVirtualMemory - (WriteMemoryAddr+5);
//将jmpaddr 写入数组E9之后4字节
wpoff(); //关闭CR0
Irql=KeRaiseIrqlToDpcLevel(); //提升IRQ中断等级
//向WriteVirtualMemory首地址写入7字节,实现HOOK
RtlCopyMemory((BYTE*)WriteMemoryAddr,JmpAddress,7);
KeLowerIrql(Irql); //恢复Irql
wpon(); //开启cr0
}
/*********************************************************
**********功能:hook原函数头5个字节跳转到自写函数执行******
**************** ******
**********参数: 无 ******
*********返回值: 无 ******
*********公式:jmp jmpaddr jmp机器码E9 ******
*********jmpaddr公式:新地址-(原地址+5)或-原地址-5 ******
*********************************************************/
void HookReadVirtualMemory()
{
BYTE JmpAddress[7] = {0xE9,0,0,0,0,0x90,0x90};
KIRQL Irql;
ReadMemoryAddr=CurNTReadVirtualMemory();
readMemory_22=ReadMemoryAddr+0x22;
*(ULONG*)(JmpAddress+1)=(ULONG)MyReadVirtualMemory - (ReadMemoryAddr+5);
wpoff();
Irql=KeRaiseIrqlToDpcLevel();
RtlCopyMemory((BYTE*)ReadMemoryAddr,JmpAddress,7);
KeLowerIrql(Irql);
wpon();
}
/*********************************************************
**********功能:hook原函数头5个字节跳转到自写函数执行******
**************** ******
**********参数: 无 ******
*********返回值: 无 ******
*********公式:jmp jmpaddr jmp机器码E9 ******
*********jmpaddr公式:新地址-(原地址+5)或-原地址-5 ******
*********************************************************/
void HookAllocateVirtualMemory()
{
BYTE Jmpaddress[5] = {0xE9,0,0,0,0};
//因AllocateVirtualMemory汇编头部为push 118占5字节 与jmp xxxx 相同 故只需hook一条指令即可
KIRQL Irql;
AllocateMemoryAddr=CurNTAllocateVirtualMemory();
AllocateMemory_5=AllocateMemoryAddr+0x5;
*(ULONG*)(Jmpaddress+1)=(ULONG)MyAllocateVirtualMemory - (AllocateMemoryAddr+5);
wpoff();
Irql=KeRaiseIrqlToDpcLevel();
RtlCopyMemory((BYTE*)AllocateMemoryAddr,Jmpaddress,5);
KeLowerIrql(Irql);
wpon();
}
该驱动代码 可根据自己系统改变硬编码后 用XT KD 可以看到加载后和卸载 HOOK的几个函数代码变化,无需使用游戏。
相信新手会对inline hook有所了解
|