注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

熊猫正正的博客

熊猫正正的天空

 
 
 

日志

 
 

Rootkit hook之[三] Inline Hook  

2012-03-16 14:04:59|  分类: window驱动学习 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
标 题: 【原创】rootkit hook之[三] inline hook
作 者: combojiang

时 间: 2008-01-30,17:27:57
链 接: http://bbs.pediy.com/showthread.php?t=59127

最近为了写好rootkit inline hook篇,特意A了著名的流氓软件(cdnprot.sys),这个文件很庞大,有152k之多, 花费了我好几个晚上的时间,让我少看好多集的电视剧《闯关东》,在这个软件里面用了很多好的技术,不管怎么说,技术本身是无辜的,由于我们今天谈论的主题是Inline hook,因此今天只是带领大家看看他是怎样使用inline hook这项技术的。今天是一年一度的小年,发表此篇,作为对大家的小年献礼吧,顺祝大家小年好。

关于什么是inline hook.,这些基本概念,我们就不在这里说了,大家可以google下。
对于ring3下的inline hook使用起来非常的方便,也非常简单。但是到了ring0,inline hook就麻烦些,搞不好就出现了bsod.Rootkit hook之[三] Inline Hook - 熊猫正正 - 熊猫正正的博客  ,我们今天讲的是内核中的inline hook。这个技术,在这个流氓软件中应用的比较稳定。我们就来看看它是怎么用的。

先谈谈思路:

1.  Hook之前的准备工作之一。
在这个软件中,总共hook了15个native api 函数。他们分别是:
ZwOpenKey , ZwClose, ZwQueryValueKey, ZwDeleteKey, ZwSetValueKey, ZwCreateKey,
ZwDeleteValueKey. ZwEnumerateValueKey,ZwRestoreKey, ZwReplaceKey, ZwTerminateProcess, ZwSetSecurityObject, ZwCreateThread, ZwTerminateThread, ZwQuerySystemInformation.

这15个函数中,包括2个未公开的函数,ZwCreateThread, ZwTerminateThread,这两个函数,需要我们从ntdll.dll的导出表中找到。另外,所有的native api函数的最终实现都是在ntoskrnl模块中,所以,我们使用ZwQuerySystemInformation的0B号功能,找出ntoskrnl模块的内存加载区间,然后逐个判断ssdt表中这些要hook的函数地址,是否在这个区间内。确保我们是第一个吃螃蟹的人。呵呵。

2.  Hook之前准备工作之二:
1)一个全局函数表 ,保存这15个要hook的函数的原始地址。
这个表起始地址位于:.data:00036860 ,终止于:data:0003689C 共60字节
2)一个hook 的函数地址表,分别对应于要hook的15个函数的跳转。
这个表起始地址位于:.data:00034E98
.data:00034E98 off_34E98        dd offset sub_1EEA8    
.data:00034E9C                 dd offset sub_1EE82
.data:00034EA0                 dd offset sub_1EF82
.data:00034EA4                 dd offset sub_1EF4A
.data:00034EA8                 dd offset sub_1EF6D
.data:00034EAC                 dd offset sub_1EEC1
.data:00034EB0                 dd offset sub_1EED2
.data:00034EB4                 dd offset sub_1EEF5
.data:00034EB8                 dd offset sub_1EF31
.data:00034EBC                 dd offset sub_1EF18
.data:00034EC0                 dd offset sub_1EF93
.data:00034EC4                 dd offset sub_1EFA8
.data:00034EC8                 dd offset sub_1EFBD
.data:00034ECC                 dd offset sub_1EFE6
.data:00034ED0                 dd offset sub_1EFFF

这15个函数,都是在cdnprot.sys中实现的。

3)一个用于保存函数开头字节的二维数组数据区,数组形式是 array[15][30];
  15个函数,每个函数有30个字节可用。 这30个字节中,首先保存原hook函数开头字节,然后写入0xe9 ,再写入数组当前位置跟原hook函数地址偏移我们已复制出的字节码后的位置之间的相对偏移值。(具体我们要保存原hook函数开头多少字节,代码中有一个算法。)
  这个数组的作用是,当我们hook了函数后,执行完我们的hook函数之后,然后,需要恢复执行原api函数,由于原api函数开头5字节已经被改写,由于函数原开头字节已经保存到相应的数组里,因此这里的作法是,执行这个数组中的机器码,数组机器码执行到最后,会跳转到原hook函数某个偏移位置,继续执行。

3.  Inline hook
1)  IoAllocateMdl ,分配一个mdl,将要hook的函数映射进去。
2)  MmProbeAndLockPages,锁定页面
3)  去掉写保护
4)  保存函数开头机器码到对应的二维数组区,改写开头5个字节,让他跳转到起始地址位于.data:00034E98的跳转表中的对应跳转函数。
5)  恢复写保护
6)  MmUnlockPages
7)  IoFreeMdl


4.  Inline hook后的恢复工作
正如2步骤3)中描述的那样。

代码太多,不在这里贴出了。
Inline hook对应的函数是sub_1F30D,这个函数很庞大。嵌套了n多层。我把逆向的源文件和.idb文件附上。有兴致的可以使用ida5.2看看。

附上一个例子吧:
这个例子是hook了KiInsertQueueApc,由于KiInsertQueueApc没有导出,需要在KeInsertQueueApc中找出来。
#include <ntddk.h> 
#include <ntifs.h> 
ULONG g_KiInsertQueueApc; 
char g_oricode[8]; 
ULONG g_uCr0; 
char *non_paged_memory; 

void WPOFF() 

ULONG uAttr; 

_asm 
push eax; 
mov eax, cr0; 
mov uAttr, eax; 
and eax, 0FFFEFFFFh; // CR0 16 BIT = 0 
mov cr0, eax; 
pop eax; 
cli 
}; 

g_uCr0 = uAttr; //保存原有的 CRO 屬性 


VOID WPON() 

_asm 
sti 
push eax; 
mov eax, g_uCr0; //恢復原有 CR0 屬性 
mov cr0, eax; 
pop eax; 
}; 


__declspec(naked) my_function_detour_KiInsertQueueApc() 
__asm 
mov edi,edi 
push ebp 
mov ebp, esp 
push ecx 
mov eax,ecx 
_emit 0xEA 
_emit 0xAA 
_emit 0xAA 
_emit 0xAA 
_emit 0xAA 
_emit 0x08 
_emit 0x00 

ULONG GetFunctionAddr( IN PCWSTR FunctionName) 
UNICODE_STRING UniCodeFunctionName; 
RtlInitUnicodeString( &UniCodeFunctionName, FunctionName ); 
return (ULONG)MmGetSystemRoutineAddress( &UniCodeFunctionName ); 


//根据特征值,从KeInsertQueueApc搜索中搜索KiInsertQueueApc 
ULONG FindKiInsertQueueApcAddress() 
char * Addr_KeInsertQueueApc = 0; 
int i = 0; 
char Findcode[] = { 0xE8, 0xcc, 0x29, 0x00, 0x00 }; 
ULONG Addr_KiInsertQueueApc = 0; 
Addr_KeInsertQueueApc = (char *) GetFunctionAddr(L"KeInsertQueueApc"); 
for(i = 0; i < 100; i ++) 
if( Addr_KeInsertQueueApc == Findcode[0] && 
Addr_KeInsertQueueApc[i + 1] == Findcode[1] && 
Addr_KeInsertQueueApc[i + 2] == Findcode[2] && 
Addr_KeInsertQueueApc[i + 3] == Findcode[3] && 
Addr_KeInsertQueueApc[i + 4] == Findcode[4] 
Addr_KiInsertQueueApc = (ULONG)&Addr_KeInsertQueueApc + 0x29cc + 5; 
break; 
return Addr_KiInsertQueueApc; 

VOID DetourFunctionKiInsertQueueApc() 

char *actual_function = (char *)g_KiInsertQueueApc; 
unsigned long detour_address; 
unsigned long reentry_address; 
KIRQL oldIrql; 
int i = 0; 

char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00, 0x90 }; 

reentry_address = ((unsigned long)g_KiInsertQueueApc) + 8; 

non_paged_memory = ExAllocatePool(NonPagedPool, 256); 

for(i=0;i<256;i++) 
((unsigned char *)non_paged_memory) = ((unsigned char *)my_function_detour_KiInsertQueueApc); 

detour_address = (unsigned long)non_paged_memory; 

*( (unsigned long *)(&newcode[1]) ) = detour_address; 

for(i=0;i<200;i++) 
if( (0xAA == ((unsigned char *)non_paged_memory)) && 
(0xAA == ((unsigned char *)non_paged_memory)[i+1]) && 
(0xAA == ((unsigned char *)non_paged_memory)[i+2]) && 
(0xAA == ((unsigned char *)non_paged_memory)[i+3])) 
*( (unsigned long *)(&non_paged_memory) ) = reentry_address; 
break; 


oldIrql = KeRaiseIrqlToDpcLevel(); 
for(i=0;i < 8;i++) 
g_oricode = actual_function; 
actual_function = newcode; 
KeLowerIrql(oldIrql); 

VOID UnDetourFunction() 
char *actual_function = (char *)g_KiInsertQueueApc; 
KIRQL oldIrql; 
int i = 0; 

WPOFF(); 
oldIrql = KeRaiseIrqlToDpcLevel(); 

for(i=0;i < 8;i++) 
actual_function = g_oricode; 
KeLowerIrql(oldIrql); 
WPON(); 
ExFreePool(non_paged_memory); 

VOID OnUnload( IN PDRIVER_OBJECT DriverObject ) 
DbgPrint("My Driver Unloaded!"); 
UnDetourFunction(); 

NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath ) 
DbgPrint("My Driver Loaded!"); 
theDriverObject->DriverUnload = OnUnload; 

g_KiInsertQueueApc = FindKiInsertQueueApcAddress(); 
DetourFunctionKiInsertQueueApc(); 

return STATUS_SUCCESS; 
}


  评论这张
 
阅读(46)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017