探索NDIS HOOK新的实现方法(1)
NDIS HOOK是专业级防火墙使用的一种拦截技术,NDIS HOOK的重点是如何获得特定协议对应NDIS_PROTOCOL_BLOCK指针,获得了该指针,接下来就可以替换该协议所注册的收发函数,而达到拦截网络数据的目的。
获 得NDIS_PROTOCOL_BLOCK指针的方法一般是用NdisRegisterProtocol注册一个新的协议,所获得的协议句柄实际上就是一 个NDIS_PROTOCOL_BLOCK指针,顺着该指针遍历NDIS_PROTOCOL_BLOCK链表,就可以找到你所要挂钩的协议所对应的 NDIS_PROTOCOL_BLOCK.之所以可以这样做,是因为每注册一个协议,系统都会把该协议对应的NDIS_PROTOCOL_BLOCK放置 在协议链表的开头,该协议链表每个元素都是NDIS_PROTOCOL_BLOCK类型,代表一个已经注册的协议。
事 实上我们需要的只是TCPIP协议族的NDIS_PROTOCOL_BLOCK指针,毕竟TCP,IP,ARP,ICMP等等几乎所有我们感兴趣的协议, 都是在tcpip.sys协议驱动里面实现的。如果我们只需要TCPIP协议所对应的NDIS_PROTOCOL_BLOCK,那么上面的方法就有点繁琐 了。我们可以试着寻找更简便的方法来获得TCPIP协议的NDIS_PROTOCOL_BLOCK.
于 是我对tcpip.sys驱动进行了反汇编,发现NDIS_PROTOCOL_BLOCK指针存放在一个名为_ARPHandle的全局变量里面,所以如 果能找到_ARPHandle的地址,我们就成功了,我们完全可以把该全局变量的偏移量作为一个常量来使用,但这里纯粹为了拓宽思路,我介绍另一种找到该 全局变量的方法。
Tcpip.sys有个导出函数叫IPDelayedNdisReEnumerateBindings,该函数内部曾经出现过_ARPHandle 的地址,为什么会出现它的地址呢,因为该函数内部调用过NdisReEnumerateProtocolBindings函数,懂得反汇编的应该知道,在 用call指令调用函数之前,必然会用到push指令将函数的参数压到栈里面去,不巧的是, NdisReEnumerateProtocolBindings函数只有一个参数,而该参数恰恰是一个NDIS_PROTOCOL_BLOCK指针类
型,在这里,实际上就是把_ARPHandle当作参数传给了
NdisReEnumerateProtocolBindings,所以_ARPHandle的地址必然会出现在push指令的后面,说具体一点,紧跟push指令的四个字节就是_ARPHandle的地址。
所以具体的思路就是这样,先找到IPDelayedNdisReEnumerateBindings函数的地址,然后从该函数的地址开始搜索push指令的特征码,搜到了以后,把紧跟push指令的四个字节作为指向NDIS_PROTOCOL_BLOCK指针的指针返回。
也许有的人会问,如果IPDelayedNdisReEnumerateBindings函数体内部出现过多次push指令,岂不是会搜出不正确的地址,事实上,虽然都叫push指令,然而在机器码级别是不同的,push指令的机器码表示有十几种之多,用来区别不同的寻址方式,调用NdisReEnumerateProtocolBindings 时用的push指令字节序列是0xff35,这个push指令表示后面紧跟的四个字节是一个内存地址,而不是一个立即数或者寄存器之类的。知道了这些,我 们就可以清楚,在一个有限的地址范围,0xff35的唯一性是可以得到满足的。根据我的观察,在win2000,winxp,win2003上面,IPDelayedNdisReEnumerateBindings本身是一个很短的函数,0xff35指令确实只出现过一次,所以该方法是很可靠的。
思路已经出来了,下面我把详细的代码给大家贴出来,理解这些代码需要对windows Pe格式有所了解,如果你不想理解也行,代码可以直接拿来用。
以下是我写的一个 获取内核模块某个导出函数地址的 通用例程。这里主要是为了获取tcpip.sys模块的导出函数IPDelayedNdisReEnumerateBindings
void* GetRoutineAddress(char* ModuleName,char* RoutineName)
{
PIMAGE_DOS_HEADER dos_hdr;
PIMAGE_NT_HEADERS nt_hdr;
PIMAGE_EXPORT_DIRECTORY export_dir;
ULONG *fn_name, *fn_addr, i;
char* base;
base=(char*)FindModule(ModuleName);//该函数用来获得内核模块的基地址
if(!base)
return NULL;
DbgPrint("tcpip address:%p",base);
dos_hdr = (PIMAGE_DOS_HEADER)base;
if (dos_hdr->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
nt_hdr = (PIMAGE_NT_HEADERS)(base + dos_hdr->e_lfanew);
export_dir = (PIMAGE_EXPORT_DIRECTORY)(base + nt_hdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
fn_name = (ULONG *)(base + export_dir->AddressOfNames);
fn_addr = (ULONG *)(base + export_dir->AddressOfFunctions);
for (i = 0; i < export_dir->NumberOfNames; i++, fn_name++, fn_addr++)
{
if (strcmp(RoutineName, base + *fn_name) == 0)
{
return base + *fn_addr;
}
}
return NULL;
}
以下是FindModule函数的实现:
void *
FindModule(char *name)
{
ULONG i, n, *q;
PSYSTEM_MODULE_INFORMATION p;
void *base;
ZwQuerySystemInformation(SystemModuleInformation, &n, 0, &n);
q = (ULONG *)ExAllocatePool(PagedPool, n);
ZwQuerySystemInformation(SystemModuleInformation, q, n * sizeof (*q), 0);
p = (PSYSTEM_MODULE_INFORMATION)(q + 1);
base = NULL;
for (i = 0; i < *q; i++) {
if (_stricmp(p[i].ImageName + p[i].ModuleNameOffset, name) == 0) {
base = p[i].Base;
break;
}
}
ExFreePool(q);
return base;
}
以下是获取tcpip协议的NDIS_PROTOCOL_BLOCK指针的函数
void* GetProtocolBlock()
{
char* base;
char bytes[]={0xff,0x35};
base=GetRoutineAddress("tcpip.sys","IPDelayedNdisReEnumerateBindings");
while(RtlCompareMemory(base,bytes,2)!=2)
{
base++;
}
return **((void***)(base+2));
}
探索NDIS HOOK新的实现方法(1)的更多相关文章
- iOS中的过期方法和新的替代方法
关于iOS中的过期方法和新的替代方法 1.获取某些类的UINavigationBar的统一外观并设置UINavigationbar的背景 注:方法名改了但是基本使用方法不变 + (instancety ...
- 利用GBDT模型构造新特征具体方法
利用GBDT模型构造新特征具体方法 数据挖掘入门与实战 公众号: datadw 实际问题中,可直接用于机器学**模型的特征往往并不多.能否从"混乱"的原始log中挖掘到有用的 ...
- 实现Square类,让其继承自Rectangle类,并在Square类增添新属性和方法,在2的基础上,在Square类中重写Rectangle类中的初始化和打印方法
实现Square类,让其继承自Rectangle类,并在Square类增添新属性和方法,在2的基础上,在Square类中重写Rectangle类中的初始化和打印方法 #import <Found ...
- 安全性良好的operator=操作,和新的new方法(针对深度复制的情况)
class B { }; class A { public: A& operator=(const A& a) { B* temp = b; //这里解决重复赋值的方法是用temp指向 ...
- java8在Collection中新增加的方法removeIf
记得我在以前找工作的经历中,遇到过一个面试官问过我一个很基础的问题.问题是:有一个List中有10个元素,我现在想从中删除3个元素,请问怎么做?我当时也没想,就直接说,List的有自带的remove方 ...
- Eviews 9.0新功能——估计方法(ARDL、面板自回归、门限回归)
每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 9.2 估计功能 eviews9.0下载链接: ...
- js给原型增加新属性和方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 乐字节-Java8新特性之方法引用
上一篇小乐介绍了<Java8新特性-函数式接口>,大家可以点击回顾.这篇文章将接着介绍Java8新特性之方法引用. Java8 中引入方法引用新特性,用于简化应用对象方法的调用, 方法引用 ...
- linux,添加新硬盘的方法
一.物理机添加一块新的硬盘方法(目的是把后加的磁盘直接加在现有的上面,不用再分区挂载)1.首先要确定现有系统在那块盘上 [root@localhost ~]# df -lhFilesystem ...
随机推荐
- python操作pymysql
#_author:来童星#date:2019/12/19import pymysql#1.打开数据库连接db=pymysql.connect('localhost','root','root','te ...
- Delphi DBgrid 动态点击事件
错误的写法: DBGrid1CellClick(DBGrid1.Columns[ DBGrid1.DataSource.DataSet.RecNo ]); //执行点击事件 正确的写法: DBGrid ...
- 浅析阿里云API网关的产品架构和常见应用场景
自上世纪60年代计算机网络发展开始,API(Application Programming Interface )随之诞生,API即应用程序接口,是实现系统间衔接的桥梁.时至今日,API市场已经形成了 ...
- cf期望概率专题
cf1009E:求到第i段期望和的比较困难,但是单独求每段的期望是比较容易的,所以单独对每段求和,然后累计总和 E[i]=1/2*a1+1/4*a2+...+1/2^(i-1)*ai-1+1/2^(i ...
- Service6
rsync同步操作 同步 : 只传输变化的数据 复制:完整的传输 • 命令用法– rsync [选项...] 源目录 目标目录 • 同步与复制的差异– 复制:完全拷贝源到目标– 同步:增量拷贝 ...
- CTSC2019
(upd:随机立方体AC) 太菜了只会部分分.以后慢慢补坑吧…… 随机立方体: 30分: 正常人都能想到的的转移状态(我的确是弱智),从大往小填数,记录有多少个极大值点和三个方向上各占了多少.转移可以 ...
- 关于ACL访问控制的一些问题:AntiACL
@echo off title AntiACL Made By gwsbhqt color 0a reg query "HKU\S-1-5-19" >nul 2>nul ...
- C++——数据结构之链表
直接上例子 int main() { ,,}; ,,}; Listnode *head=NULL,*temp; head=(Listnode*)malloc(sizeof(Listnode));//头 ...
- 牛客 判断t1树中是否含有与t2树拓扑结构完全相同的子树
题目链接:https://www.nowcoder.com/practice/5a41ce17e8194e1688aa83a73137f7ee?tpId=101&tqId=33239& ...
- OSPF中DR和BDR到底是谁先选举出来的?
在OSPF的DRBDR选举的过程中,DR的选举依靠的是hello报文,在two-way之后,交互hello报文完成DR/BDR的选举. 那么在每台路由器根据收到的所有hello报文,会构建自己接口的数 ...