Jesses

集中精神
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

inline hook 深层call ObOpenObjectByPointe的实现及用SSDT反HOOK

Posted on 2010-01-05 19:00  Jesses  阅读(2636)  评论(0编辑  收藏  举报

 

最近研究TESSAFE.SYS的驱动,GOOGLE搜索到微点也是一样的手法HOOK call ObOpenObjectByPointe,把不完整的代码给编写实现了下。未实现检测,应该是有修改我的代码或恢复HOOK call即重启或蓝屏,我为了方便没实现,下载我改动后驱动文件,用来保护记事本不被OPENprocess。。

然后unhook call ObOpenObjectByPointe 的代码从网上摘来,在搞虚拟机里可以过掉,但主机确是老蓝屏8e代码。原来是代码根本不对,看雪上发的贴,有指出不对的代码拷贝函数,我做下修改为指令长度拷贝,终于可以了。反inline hook 深层call驱动文件,可以下载,源码都在下面了,文件里就没放。注意顺序,必须先开这个驱动才有用,因为是从内存拷贝来的,先让inline hook 深层call驱动加载了,在拷贝内存也是错误的。关闭时倒序来,否则蓝了别怪我,模拟就只是模拟罢了。

//unhok call 这是SSDT hook,在深层call ObOpenObjectByPointe驱动加载前复制NtOpenThread NtOpenProcess 两个代码到别处SSDT。原来主机蓝屏,但虚拟机也实现不蓝,反汇编下看到代码烤过来的不对,果然是拷贝函数没处理细节,做下修改BufferCode函数加了反汇编引擎判断指令长度,按长度来拷贝,再根据指令做重定位。

 

#include "ntddk.h"

#include <ntdef.h>

#include "LDasm.h"

#define ThreadLength 0x4f4 //要保存的 NtOpenThread 原代码的长度

#define ProcessLength 0x28a //要保存的 NtOpenProcess 原代码的长度


#define DeviceLink L"\\Device\\DNFCracker"

#define SymbolicLink L"\\DosDevices\\DNFCracker"

#define IOCTL_RESTORE (ULONG)CTL_CODE(FILE_DEVICE_UNKNOWN, 0x886, METHOD_BUFFERED, FILE_ANY_ACCESS)


typedef NTSTATUS (* NTOPENTHREAD)(

 OUT PHANDLE ThreadHandle,

 IN ACCESS_MASK DesiredAccess,

 IN POBJECT_ATTRIBUTES ObjectAttributes,

 IN OPTIONAL PCLIENT_ID ClientId

 );


typedef NTSTATUS (* NTOPENPROCESS)(

  OUT PHANDLE ProcessHandle,

  IN ACCESS_MASK DesiredAccess,

  IN POBJECT_ATTRIBUTES ObjectAttributes,

  IN PCLIENT_ID ClientId

  );


typedef struct _SERVICE_DESCRIPTOR_TABLE

{

PVOID    ServiceTableBase;

PULONG   ServiceCounterTableBase;

ULONG    NumberOfService;

ULONG    ParamTableBase;

}

SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE;

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;


VOID Hook();

VOID Unhook();

VOID BufferCode(PUCHAR pCode, ULONG TrgAddr, ULONG BufferLength);

NTOPENTHREAD OldThread;

NTOPENPROCESS OldProcess;

ULONG AddrRead, AddrWrite;

//原 NtReadVirtualMemory/NtWriteVirtualMemory 的前 16 字节代码

ULONG OrgRead[2], OrgWrite[2];

//保存 NtOpenThread/NtOpenProcess 代码

UCHAR MyThread[ThreadLength], MyProcess[ProcessLength];


NTSTATUS MyNtOpenThread(

PHANDLE ThreadHandle,

ACCESS_MASK DesiredAccess,

POBJECT_ATTRIBUTES ObjectAttributes,

PCLIENT_ID ClientId) 

{

ACCESS_MASK oDA;

OBJECT_ATTRIBUTES oOA;

CLIENT_ID oCID;

NTSTATUS statusF, statusT;


oDA = DesiredAccess;

oOA = *ObjectAttributes;

oCID = *ClientId;


statusF = OldThread(ThreadHandle, oDA, &oOA, &oCID);

statusT = ((NTOPENTHREAD)MyThread)(ThreadHandle, DesiredAccess, ObjectAttributes, ClientId);

return statusT;

}


NTSTATUS MyNtOpenProcess(

PHANDLE ProcessHandle,

ACCESS_MASK DesiredAccess,

POBJECT_ATTRIBUTES ObjectAttributes,

PCLIENT_ID ClientId) 

{

ACCESS_MASK oDA;

OBJECT_ATTRIBUTES oOA;

CLIENT_ID oCID;

NTSTATUS statusF, statusT;


oDA = DesiredAccess;

oOA = *ObjectAttributes;

oCID = *ClientId;


statusF = OldProcess(ProcessHandle, oDA, &oOA, &oCID);

statusT = ((NTOPENPROCESS)MyProcess)(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);

return statusT;

}



VOID OnUnload(IN PDRIVER_OBJECT DriverObject)

{

//UNICODE_STRING usLink;

/*ULONG i;

for (i = 0; i < ThreadLength; i += 4)

{

DbgPrint("%02x %02x %02x %02x\n", MyThread[i], MyThread[i + 1], MyThread[i + 2], MyThread[i + 3]);

DbgPrint("%02x %02x %02x %02x\n", MyProcess[i], MyProcess[i + 1], MyProcess[i + 2], MyProcess[i + 3]);

}

*/

Unhook();

DbgPrint("DNF Cracker Unloaded!");

}


VOID Hook()

{

ULONG AddrProcess, AddrThread;

KIRQL Irql;

AddrRead = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0xBA * 4;

AddrWrite = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x115 * 4;

AddrThread = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x80 * 4;

AddrProcess = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;


OldThread = (NTOPENTHREAD)(*(PULONG)AddrThread);

OldProcess = (NTOPENPROCESS)(*(PULONG)AddrProcess);

//DbgPrint("MyThread:0x%08X OldThread:0x%08X", MyThread, OldThread);

//DbgPrint("MyProcess:0x%08X OldProcess:0x%08X", MyProcess, OldProcess);


__asm

{

cli

mov eax,cr0

and eax,not 10000h

mov cr0,eax

}

Irql=KeRaiseIrqlToDpcLevel();

//记录 NtReadVirtualMemory/NtWriteVirtualMemory 前 16 字节

/* OrgRead[0] = *(PULONG)(*(PULONG)AddrRead);

OrgRead[1] = *(PULONG)(*(PULONG)AddrRead + 4);

OrgWrite[0] = *(PULONG)(*(PULONG)AddrWrite);

OrgWrite[1] = *(PULONG)(*(PULONG)AddrWrite + 4);*/


//保存原代码

BufferCode(MyThread, (ULONG)OldThread, ThreadLength);

BufferCode(MyProcess, (ULONG)OldProcess, ProcessLength);


//SSDT Hook

*(PULONG)AddrThread = (ULONG)MyNtOpenThread;

*(PULONG)AddrProcess = (ULONG)MyNtOpenProcess;

KeLowerIrql(Irql);

__asm

{

mov eax,cr0

or eax,10000h

mov cr0,eax

sti

}

DbgPrint("OldThread!:%08X\n",(ULONG)OldThread);

DbgPrint("OldProcess!:%08X\n",(ULONG)OldProcess);

DbgPrint("MyThread!:%08X\n",(ULONG)MyThread);

DbgPrint("MyProcess!:%08X\n",(ULONG)MyProcess);

}


VOID Unhook()

{

ULONG AddrProcess, AddrThread;KIRQL Irql;

AddrThread = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x80 * 4;

AddrProcess = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;


__asm

{

cli

mov eax,cr0

and eax,not 10000h

mov cr0,eax

}

Irql=KeRaiseIrqlToDpcLevel();

//恢复 SSDT

*(PULONG)AddrThread = (ULONG)OldThread;

*(PULONG)AddrProcess = (ULONG)OldProcess;

KeLowerIrql(Irql);

__asm

mov eax,cr0

or eax,10000h

mov cr0,eax

sti

}

}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)

{

DbgPrint("DNF Cracker Loaded!");

DriverObject->DriverUnload = OnUnload;


Hook();


return STATUS_SUCCESS;

}


// OrgRel 原相对跳转地址

// CurAbs 当前代码绝对地址

// MyAbs 替换代码绝对地址

// CodeLen 跳转代码占据的长度

// 返回值 到替换代码的相对地址

LONG GetRelAddr(LONG OrgRel, ULONG CurAbs, ULONG MyAbs) //, ULONG CodeLen)

{

ULONG TrgAbs;

TrgAbs = CurAbs + OrgRel; // + CodeLen; //目的地址

return TrgAbs - MyAbs;

}


// 保存原来整个函数的代码(已修改的正确拷贝指令函数)

// pCode 用来保存代码的数组的地址

// TrgAddr 要保存的函数的地址

// BufferLength 整个函数占用的大小

VOID BufferCode(PUCHAR pCode, ULONG TrgAddr, ULONG BufferLength)

{

PUCHAR cPtr, pOpcode;

ULONG cAbs, i;

LONG oRel, cRel;

    ULONG Length;

memset(pCode, 0x90, BufferLength);

for (i = 0; i < BufferLength; i+= Length)

{

cAbs = TrgAddr + i;

pCode[i] = *(PUCHAR)cAbs;

Length = SizeOfCode((PUCHAR)cAbs, &pOpcode);//計算當前指令長度

if(Length)

{//不为0则考过来指令, 长度:Length

memcpy(pCode + i, (PVOID)(cAbs), Length);

}//下面对这条指令重新处理,修正定位

//if (!Length) break;

switch (*(PUCHAR)cAbs)

{

case 0x0F: //JXX NEAR X

if ((*(PUCHAR)(cAbs + 1) >= 0x80)&&(*(PUCHAR)(cAbs + 1) <= 0x8F))

{

oRel = *(PLONG)(cAbs + 2);

if ((oRel + cAbs + 6 > TrgAddr + BufferLength)||

(oRel + cAbs + 6 < TrgAddr)) //判断跳转是否在过程范围内

{

pCode[i + 1] = *(PUCHAR)(cAbs + 1);

cRel = GetRelAddr(oRel, cAbs, (ULONG)pCode + i);

memcpy(pCode + i + 2, &cRel, sizeof(LONG));

//DbgPrint("JXX: 0x%08X -> 0x%08X", cAbs, (ULONG)pCode + i);

//i += sizeof(LONG) + 1;

}

}

break;

case 0xE8: //CALL

oRel = *(PLONG)(cAbs + 1);

if ((oRel + cAbs + 5 > TrgAddr + BufferLength)||

(oRel + cAbs + 5 < TrgAddr)) //判断跳转是否在过程范围内

{

cRel = GetRelAddr(oRel, cAbs, (ULONG)pCode + i);

memcpy(pCode + i + 1, &cRel, sizeof(LONG));

//DbgPrint("CALL: 0x%08X -> 0x%08X", cAbs, (ULONG)pCode + i);

}

break;

case 0x80: //CMP BYTE PTR X

if (*(PUCHAR)(cAbs + 1) == 0x7D)

{

memcpy(pCode + i + 1, (PVOID)(cAbs + 1), 3);

//i += 3; 

continue;

}

break;

case 0xC2: //RET X

if (*(PUSHORT)(cAbs +1) == 0x10)

{

memcpy(pCode + i + 1, (PVOID)(cAbs + 1), sizeof(USHORT));

//i += sizeof(USHORT);

}

break;

/*case 0xE9: //JMP

oRel = *(PLONG)(cAbs + 1);

if (oRel + cAbs > TrgAddr + BufferLength)

{

cRel = GetRelAddr(oRel, cAbs, (ULONG)pCode + i);

memcpy(pCode + i + 1, &cRel, sizeof(LONG));

i += 4;

}*/

//default:

}

/*下面的处理都不必用了

if ((*(PUCHAR)cAbs == 0x39)||(*(PUCHAR)cAbs == 0x89)||(*(PUCHAR)cAbs == 0x8D))

{

memcpy(pCode + i + 1, (PVOID)(cAbs + 1), sizeof(USHORT));

i += sizeof(USHORT);

continue;

}*/

//DbgPrint("addr:%08X//n:%02X//Length!%08X\n",(ULONG)cAbs,*(PUCHAR)cAbs,Length);

/*if ((*(PUCHAR)cAbs >= 0x70)&&(*(PUCHAR)cAbs <= 0x7F)&&(*(PUCHAR)(cAbs - 1) != 0xFF))

{

oRel = (LONG)(*(PCHAR)(cAbs + 1));

cRel = GetRelAddr(oRel, cAbs, (ULONG)pCode + i);

memcpy(pCode + i + 1, &cRel, 1);

i++; continue;

}*/

}

}

//可惜这个代码早已放出来很久,TX更新很快,早没用了,开启来游戏就它提示非法,辛亏没缺德到蓝屏重启。然后又加个INLINE HOOK NTOPENPROCESS 头,可惜被检测到。

//不过学习这些还是有好处的,不经意间注意到一个细节被我过了,哈哈。。 

 

----------------------------------------------------

下面是 原来网上hook 深层call ObOpenObjectByPointe的实现代码 源自于微点

 

代码
#include "NTDDK.H"
#include 
"ntimage.h"
#include 
"windef.h"

#include 
"LDasm.h"

#define NT_DEVICE_NAME L"\\Device\\Hook"
#define DOS_DEVICE_NAME L"\\DosDevices\\Hook"


NTKERNELAPI PEPROCESS    IoThreadToProcess (IN PETHREAD Thread);

typedef NTSTATUS (
*OBOPENOBJECTBYPOINTER)
(
IN PVOID Object,
IN ULONG HandleAttributes,
IN PACCESS_STATE PassedAccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PHANDLE Handle
);OBOPENOBJECTBYPOINTER pObOpenObjectByPointer
=NULL;


PVOID pNtOpenProcess
=NULL;
PVOID pNtOpenThread
=NULL;

PEPROCESS MyProcess
=(PEPROCESS)0x822ab458;//我們要保護的進程對象 自行修改

void MemOpen()
{
__asm { 
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
}

void MemClose()
{
__asm { 
mov eax,cr0
or eax,10000h
mov cr0,eax
sti

}

ULONG GetFuncAddr(PWCHAR funcname)
{
UNICODE_STRING uniFuncName;
RtlInitUnicodeString(
&amp;amp;uniFuncName,funcname);
return (ULONG)MmGetSystemRoutineAddress(&amp;amp;uniFuncName);
}


//爲NtOpenThread準備的 
NTSTATUS MyObOpenObjectByPointer_forThread(IN PVOID Object,IN ULONG HandleAttributes,
IN PACCESS_STATE PassedAccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,OUT PHANDLE Handle)
{

if (IoThreadToProcess(Object)==MyProcess)
{
return STATUS_ACCESS_DENIED;
}
else
{

return pObOpenObjectByPointer (Object, HandleAttributes,PassedAccessState,
DesiredAccess,ObjectType,AccessMode,Handle);
}
}

//NtOpenProcess
NTSTATUS MyObOpenObjectByPointer_forProcess(IN PVOID Object,IN ULONG HandleAttributes,
IN PACCESS_STATE PassedAccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,OUT PHANDLE Handle)
{

if (Object==MyProcess)
{
return STATUS_ACCESS_DENIED;
}
else
{

return pObOpenObjectByPointer (Object, HandleAttributes,PassedAccessState,
DesiredAccess,ObjectType,AccessMode,Handle);
}
}

// 從StartAddr地址 開始找OldAddr 替換爲NewAddr地址 長度是 SIZE 
BOOL CallAddrHook(PVOID StartAddr,PVOID OldAddr,PVOID NewAddr,ULONG Size)
{
PUCHAR cPtr, pOpcode;
ULONG Length,Tmp;
for (cPtr=StartAddr;(ULONG)cPtr<(ULONG)StartAddr+Size;cPtr += Length)
{
Length 
= SizeOfCode(cPtr, &pOpcode);//計算當前指令長度
if (!Length) break;
if (Length ==5 && *cPtr==0xE8)// 當前長度5 且第一字節爲E8 
{//因爲CALL用的是相對偏移 所以我們還需要進行計算相對偏移
if ( (ULONG)OldAddr-(ULONG)cPtr-5 == *(PULONG)(cPtr+1)) //判斷當前是否爲OldAddr的CALL相對地址 
{
Tmp
=(ULONG)NewAddr-(ULONG)cPtr-5;//我們的CALL地址相對偏移
MemOpen();
*(PULONG)(cPtr+1)=Tmp;//直接替換爲我們的FAKE函數地址 (微點在這裏不是直接替換它的FAKE地址 還加了一層跳闆) 
MemClose();
return TRUE;
}
}
}
return FALSE;
}