论坛: 原创软件 标题: 谁有兴趣做一个网络防火墙? 复制本贴地址    
作者: tabris17 [tabris17]    论坛用户   登录
我一直用一个叫KFW的网络防火墙,今天把它“肢解”了一下,发现它的应用层拦截通过TDI filter实现,数据层拦截通过NDIS Intermediate Driver实现,这两个技术的代码网上有很多,讲得也很详细,做起来就不知……。

人家绿盟,(前)whitecell都有自己的防火墙,我们20cn是不是也做个?

应用层可以用LSP嘛,简单一点。数据层用NDIS HOOK,实现起来应该没什么问题


地主 发表时间: 04-01-16 15:45

回复: sinister [sinister]   论坛用户   登录
利用 TDI FILTER DRIVER 主要是为得到访问网络的进程, IMD 主要是过滤协议。
如果以前对 DRIVER 没有什么过多接触,我建议用 NDIS HOOK 来实现,因为它相对
处理 DRIVER 的内核对象少,别看 TDI FILTER 比他层次高,可处理的内核对象不少,
(题外话)TDI CLIENT 就更多了。关于 IMD 我建议没有一定的 DRIVER 开发经验,
还是不要上来用它练手,因为它流程相对比较复杂,调试起来也比较困难。

WSS 的 FW 发布的那个是用 IPFILTER DRIVER 做的,相对来说很简单。第一个版本发布没
两个礼拜,我又分别用 TDI FILTER 与 NDIS HOOK (FAKE PROTOCOL) 在内核实现了相同
功能,后来因为其他事情和时间原因,应用层代码和界面一拖再拖。等到腾出功夫来了。
发现已经有人公布了这方面代码,而且写的比我要好。所以也就放弃更新了。

其实写过一段时间的 DRIVER 后,就会感到并不复杂,但想要绝对稳定还是很困难的。我们
的商业版 文件驱动 花了将近一年时间,才完全彻底的稳定下来。当然如果只是自己做着
玩,不用于商业用途那就是另说的了。

B1层 发表时间: 04-01-18 04:08

回复: NetDemon [netdemon]   ADMIN   登录
其实很早我就有这个想法,在去年的时候,我就想把BSD的IFPW移植到Windows上,linux的ipfw也是移植自BSD的,ipfw相对于当前*NIX流行的ipfilter ipchins iptables在防火墙功能上并不弱,只是他没有在内核级完成转发,NAT还需要借助另外的一个进程完成,但相对还更为简单了。
不过在第3天,我就放弃了,因为我不知道可以插到Windows的哪个位置去,我对Windows并不熟,看起来这似乎比移植到linux难多了,不过如果对windows的DRIVER熟悉的话,说不定也很容易,如果你有兴趣大家不妨研究一下,因为如果能把ipfw弄过来,这个防火墙在功能和性能上不会输给当前的其他基于Windows的防火墙。

B2层 发表时间: 04-01-18 18:11

回复: hacker521 [hacker521]   论坛用户   登录
最近开始学习编程了,可惜还停留在初始阶段,写不出什么好东西,如果在此文中发现错误的,请给我留言,我在此表示感谢!
一个简单网页防火墙的编程实现
下面要实现一个简单网页防火墙的功能,此页面只限制局域网内部的用户进行访问,如果是局域网以外的用户访问则需要输入用户名和密码。(在此假设局域网内部IP为61.57.194.!到61.57.195!之间)在此要使用到reguest对象的ServerVariables属性。
程序源码(firewall.asp)如下:
  <html>
  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=gb_2312-80">
<meta name="GENERATOR" content="Microsoft FrontPage Express 5.0">
<title> firewall.asp </title>
</head>
<body background="#800080 ">
<%                           
remoteip=Request.ServerVariables("REMOTE_ADDR")        使用Request.ServerVariables("REMOTE_ADDR")
                                                      得到IP地址并保存在变量remote中
stip=cstr(remoteip)
  for i=1 to 2
stip=right(stip,len(stip)-instr(1,stip,"."))
  next
stip=left(stip,instr(1,stip,".")-1)
if (left(remoteip,5)  <>  "61.57" or stip <"194" or stip> "195")then
username=reguest.form("t1")
password=reguest.form("t2")
Set fs = CreateObject("Scripting.FileSystemObject")
Set thisfile = fs.OpenTextFile("dsn.txt")
db_loc=thisfile.readline
thisfile.close
cnstr=db_loc&&"uid="&&username&&"; "&&"pid="&&password
on error resume next
set cn=server.createobject("adodb.connection")
cn.open cnstr
if err=3709 then %>
<p> <font color="#FF0000">  对不起,用户: <%=username%>没有访问权限,或密码不正确! <BR> </font>
</BR>
<form method="POST">
<p align="center"> 用户名: <input type="text" name="T1" size="20">
口令: 〈input type="password" name="T2" size="20"><input type="submit" value="提交" name="B1">
<input type="reset" value="全部重写" name="B2">
  </p>
  </form>
  <%end if
  cn.close
  set cn=nothing%>
  <%else %>
恭喜你,你已经通过了验证,可以直接使用本站点的资源了!
  <%end if%>
  </body>
  </html>

稍微修改一下上面如IP地址等信息,该程序就可以运行了。



B3层 发表时间: 04-01-19 14:38

回复: TomyChen [quest]   版主   登录
楼上的,根本不是一个量级上的东西...那个ASP只是相当于正则表达式的一个匹配的东西。。。。。
一个防火墙是从驱动层到应用层的东西...

B4层 发表时间: 04-01-19 17:12

回复: NetDemon [netdemon]   ADMIN   登录

你这是网页密码保护,而且只是当前这一页而已
和防火墙分马牛不相及阿

B5层 发表时间: 04-01-19 17:48

回复: newekin [newekin]      登录


[此贴被 绝对零度(newekin) 在 01月19日17时59分 编辑过]

B6层 发表时间: 04-01-19 17:58

回复: hacker521 [hacker521]   论坛用户   登录
唉!我不是说我刚学吗,这个网页防火墙只能对一般IP进行限制,并不能达到真正的防火墙的目的,我只是以自己学到的一点编程知识提出一个概念而已,要真正做个防火墙,以我现在学到的编程知识还早呢

B7层 发表时间: 04-01-19 22:07

回复: tabris17 [tabris17]   论坛用户   登录


把xfocus上的那篇“基于IMD的包过滤防火墙原理与实现”翻出来看了遍,觉得作者对这个好象也不是很懂的样子。
我依着passthru例程,改了几下,蓝屏一次。win2000下的蓝屏我还是第一次看到,怕死了,我的光驱刚坏,不能重装系统,要是系统崩溃的话就阿门了。
在调试NDIS hook的时候,系统莫名重启一次。

看来驱动还是少玩玩的好

B8层 发表时间: 04-01-19 22:19

回复: hannyu [hannyu]   论坛用户   登录
给大家推荐一本书《Windows防火墙与网络封包截获技术》,通过费尔安全防火墙的基本驱动源代码,很不错。

B9层 发表时间: 04-01-20 09:53

回复: TomyChen [quest]   版主   登录
如果配置好的话,建议装个VM做测试。。。:)

xfocus上那个是基于SPI的吧?


B10层 发表时间: 04-01-20 10:47

回复: tabris17 [tabris17]   论坛用户   登录
SPI的那篇是TOo2y写的,IMD那篇是xiaobai写的

B11层 发表时间: 04-01-20 15:11

回复: tabris17 [tabris17]   论坛用户   登录
照着flashsky的那篇写了个驱动,是基于IPFILTERDRIVER的,停止服务后,一接收数据包就重启


可能是unloaddriver没处理好.我要找本讲驱动的基础书看看了

B12层 发表时间: 04-01-20 15:15

回复: TomyChen [quest]   版主   登录
找到了丢我一份...:)

B13层 发表时间: 04-01-20 16:34

回复: ricky [ricky]   版主   登录
在NDIS层面做比较好,比较方便,不过现在洗手了,就看各位了。
to四不象,要搞底层的东东,驱动是一定要学的,等你掌握了驱动这个东东,WIN在你面前基本上就是什么都没穿的美女了。
驱动就是上手调试麻烦一点,过了这个坎,后面就是读源码了。

B14层 发表时间: 04-01-20 16:47

回复: tabris17 [tabris17]   论坛用户   登录
刚在china-pub买了本书,“windows2000 wdm设备驱动程序开发”,竟然是讲driverworks的,郁闷。

to:和尚,写驱动真痛苦

B15层 发表时间: 04-01-20 18:51

回复: sinister [sinister]   论坛用户   登录
这东西光靠看代码还是不行,得调试。用 Symbol Loader 先把你的 DRIVER LOAD 进来后
Translate 带 source 的 .nsm 文件,在 SOFTICE 中 file * 找到你的 source,然后
file xxx.c 就可设断点,在 KeBugCheck() 的时候 SOFTICE 会弹出,这个时候 CALL STACK。
在 u .text+... 就可以基本定位到是哪行源代码出错。

写 DRIVER 需要注意不少问题。首先内核堆栈是很有限的,才 10 几K。定义数组的时候需
要注意,还有就是动态分配内存的时候,也要注意你分配的 POOL 类型,在 DISTATCH LEVEL
上是不允许内存交换的,也就是不能使用分页内存,在这个级别上也不允许 KeWaitForSingle
Object 等函数运行等待任何事件。还有就是当前的 IRQL 级别,有些函数不能在高于
PASSIVE LEVEL 上运行。再有就是要花不少心思在 IRP 的走向与完成的过程。蓝屏的时候要
注意观察提示信息,这样有益帮你确定问题所在。最后提醒一点,在 DRIVER 中尽量不要用
C 函数库中的函数,可以用 RtlXXX 来替代。否则的话,也可能导致,“未处理的内核异常”
等错误。


头几次看书的时候肯定不大注意这些方面,有些也是想不到的,按照我的提示多看几次书,
应该有不少帮助。总之在内核写代码不象应用层那么随便,需要考虑不少东西。

B16层 发表时间: 04-01-22 21:05

回复: tabris17 [tabris17]   论坛用户   登录
谢谢sinister的指导

我用ipfilterdriver实现了一个简单的防火墙,内置几条规则,不支持自定义规则。和wssfw差不多。吼吼~~~


现在在找NDIS HOOK的资料,大多都是要钱的,郁闷~~~~~~!

有谁知道在驱动中如何使用SEH?
是不是和user mode下一样?

B17层 发表时间: 04-01-29 12:59

回复: sinister [sinister]   论坛用户   登录
将你的 MAIL 留下。我可以将 WSSFW 利用 NDIS HOOK 那版的源代码发给你,供你参考。

在 DRIVER 中可以用 try / except 来捕获异常。但内核 SEH 并不象应用层 SEH 那样
可以恢复所有错误,一般只在 PASSIVE_LEVEL 下起作用。如在 DISPATCH_LEVEL 级你
使用了分页内存,那么即使使用了内核 SEH 还是照样会 BSOD。还有访问地址时最好先
用 MmIsAddressValid() 来确定地址是否有效。

B18层 发表时间: 04-01-29 15:22

回复: tabris17 [tabris17]   论坛用户   登录
谢谢sinister大哥

tabris17@21cn.com

我在几个俄国网站(还好有E文版的)上找到一些和ndis hook有关的东东,俄国人真是NB啊~~~

还有啊,这么重要的一条竟然没人提醒我~~~5555555~~~~

In Windows NT 4.0 this technology will work fine, however, having started this driver under Windows 2000 or XP (and if large 4MB pages are not used, if used WP protection disabled), you will see only "the dark blue screen of death". The problem is that Windows 2000 protects a kernel image from possible modification. To resolve this problem there are two possible ways:

1) Disable protection of a kernel image through the registry. For this purpose it is necessary to create in a HKLM\SYSTEM\CurrentControlSet\Control\S essionManager\Memory Management key REG_DWORD parameter with name EnforceWriteProtection and value 0.
2) Reset Write Protection bits in register CR0, before updating the export table. It can be done, for example as follows:
mov ebx, cr0; to receive current state Cr0push ebx; to save it(him)
  and ebx, ~0x10000; to reset WP bats
  mov cr0, ebx; to switch off write protection
; Here we modify the table of export....
pop ebx;
  mov cr0, ebx; to restore the previous processor state


B19层 发表时间: 04-01-29 16:29

回复: sinister [sinister]   论坛用户   登录
关于内核地址写保护问题,在 《内核级HOOK的几种实现与应用》 中提到过,原文如下。
现在看来文章还有不少欠缺与不足,关于得到内核模块地址,其实是提供了一个未公开
的函数,只不过当时我不知道罢了,还有关于修改 内核地址保护,不只这两种方法,还
可以通过分配一个 MDL 并将其设置为写方式,即可针对此地址进行写操作。此方法无需
改变全局状态,可针对某块地址,是种不错的选择。




内核级HOOK的几种实现与应用


Author : sinister

Email  : sinister@whitecell.org

HomePage: http://www.whitecell.org



  实现内核级 HOOK 对于拦截、分析、跟踪系统内核起着致关重要的作用。实现的方法不同意味着应用侧重点的不同。如想要拦截 NATIVE API 那么可能常用的就是 HOOK SERVICE TABLE 的方法。如果要分析一些系统调用,那么可能想到用 HOOK INT 2E 中断来实现。如果想要拦截或跟踪其他内核 DRIVER 的调用,那么就要用到HOOK PE 的方法来实现。这里我们更注重的是实现,原理方面已有不少高手在网上发表过文章。大家可以结合起来读。下面以我写的几个实例程序来讲解一下各种方法的实现。错误之处还望各位指正。



1、HOOK SERVICE TABLE 方法:

  这种方法对于拦截 NATIVE API 来说用的比较多。原理就是通过替换系统导

出的一个 SERVICE TABLE 中相应的 NATIVE API 的地址来达到拦截的目的。

因为此方法较为简单,网上也有不少资料来介绍。所以这里就不给出实例程序了。SERVICE TABLE 的结构如下:


typedef struct ServiceDescriptorEntry {

  unsigned int *ServiceTableBase;

  unsigned int *ServiceCounterTableBase;

  unsigned int NumberOfServices;

  unsigned char *ParamTableBase;

} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;

 


2、HOOK INT 2E 方法:

  这种方法对于跟踪、分析系统调用来说用的比较多。原理是通过替换 IDT

表中的 INT 2E 中断,使之指向我们自己的中断服务处理例程来实现的。掌握

此方法需要你对保护模式有一定的基础。下面的程序演示了这一过程。



/*****************************************************************

文件名    : WssHookInt2e.c

描述    : 系统调用跟踪

作者    : sinister

最后修改日期 : 2002-11-02

*****************************************************************/


#include "ntddk.h"

#include "string.h"


#define DWORD unsigned __int32

#define WORD unsigned __int16

#define BYTE unsigned __int8

#define BOOL __int32


#define LOWORD(l)      ((WORD)(l))

#define HIWORD(l)      ((WORD)(((DWORD)(l) >> 16) & 0xFFFF))

#define LOBYTE(w)      ((BYTE)(w))

#define HIBYTE(w)      ((BYTE)(((WORD)(w) >> 8) & 0xFF))


#define MAKELONG(a, b) ((LONG) (((WORD) (a)) | ((DWORD) ((WORD) (b))) << 16))


#define SYSTEMCALL 0x2e

#define SYSNAME "System"

#define PROCESSNAMELEN 16


#pragma pack(1)


//定义 IDTR

typedef struct tagIDTR {

    WORD IDTLimit;

    WORD LowIDTbase;

    WORD HiIDTbase;

}IDTR, *PIDTR;


//定义 IDT

typedef struct tagIDTENTRY{

  WORD OffsetLow;

  WORD selector;

  BYTE unused_lo;

  unsigned char unused_hi:5;

  unsigned char DPL:2;

  unsigned char P:1;

  WORD OffsetHigh;

} IDTENTRY, *PIDTENTRY;



#pragma pack()


DWORD  OldInt2eService;

ULONG  ProcessNameOffset;

TCHAR  ProcessName[PROCESSNAMELEN];


static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);

ULONG GetProcessNameOffset();

VOID GetProcessName( PCHAR Name );

VOID InstallNewInt2e();

VOID UninstallNewInt2e();


VOID __fastcall NativeApiCall()

{

  KIRQL OldIrql;

 

  DWORD ServiceID;

  DWORD ProcessId;


  __asm mov ServiceID,eax;



  ProcessId = (DWORD)PsGetCurrentProcessId();

  GetProcessName(ProcessName);


  KeRaiseIrql(HIGH_LEVEL, &OldIrql); // 提升当前的 IRQL 级别防止被中断



  switch ( ServiceID )

  {

      case 0x20:

        DbgPrint("NEWINT2E: ProcessName: %s; ProcessID: %d; Native Api: NtCreateFile() \n",ProcessName,ProcessId);

        break;


      case 0x2b:

        DbgPrint("NEWINT2E: ProcessName: %s; ProcessID: %d; Native Api: NtCreateSection() \n",ProcessName,ProcessId);       

        break;



      case 0x30:

        DbgPrint("NEWINT2E: ProcessName: %s; ProcessID: %d; Native Api: NtCreateToken() \n",ProcessName,ProcessId);       

        break;

       

  }


  KeLowerIrql(OldIrql); //恢复原始 IRQL


}


__declspec(naked) NewInt2eService()

{

  __asm{

    pushad

    pushfd

    push fs

    mov bx,0x30

    mov fs,bx

    push ds

    push es


    sti

    call NativeApiCall; // 调用记录函数

    cli


    pop es

    pop ds

    pop fs

    popfd

    popad


    jmp  OldInt2eService; //跳到原始 INT 2E 继续工作

  }

}


VOID InstallNewInt2e()

{


  IDTR    idtr;

  PIDTENTRY  OIdt;

  PIDTENTRY  NIdt;


  //得到 IDTR 中得段界限与基地址

  __asm {

    sidt idtr;

  }


  //得到IDT基地址

  OIdt = (PIDTENTRY)MAKELONG(idtr.LowIDTbase,idtr.HiIDTbase);


  //保存原来的 INT 2E 服务例程

  OldInt2eService = MAKELONG(OIdt[SYSTEMCALL].OffsetLow,OIdt[SYSTEMCALL].OffsetHigh);

 

  NIdt = &(OIdt[SYSTEMCALL]);


  __asm {

    cli

    lea eax,NewInt2eService; //得到新的 INT 2E 服务例程偏移

    mov ebx, NIdt;

    mov [ebx],ax;  //INT 2E 服务例程低 16 位

    shr eax,16  //INT 2E 服务例程高 16 位

    mov [ebx+6],ax;

    lidt idtr //装入新的 IDT

    sti

  }


}


VOID UninstallNewInt2e()

{

  IDTR    idtr;

  PIDTENTRY  OIdt;

  PIDTENTRY  NIdt;


  __asm {

    sidt idtr;

  }


  OIdt = (PIDTENTRY)MAKELONG(idtr.LowIDTbase,idtr.HiIDTbase);


  NIdt = &(OIdt[SYSTEMCALL]);


  _asm {

    cli

    lea eax,OldInt2eService;

    mov ebx, NIdt;

    mov [ebx],ax;

    shr eax,16

    mov [ebx+6],ax;

    lidt idtr

    sti

  }


}





// 驱动入口

NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )

{

 

  UNICODE_STRING nameString, linkString;

  PDEVICE_OBJECT deviceObject;

  NTSTATUS    status;

  HANDLE    hHandle;

  int        i;

 


  //卸载驱动

  DriverObject->DriverUnload = DriverUnload;


  //建立设备

  RtlInitUnicodeString( &nameString, L"\\Device\\WssHookInt2e" );

 

  status = IoCreateDevice( DriverObject,

              0,

              &nameString,

              FILE_DEVICE_UNKNOWN,

              0,

              TRUE,

              &deviceObject

              );

             


  if (!NT_SUCCESS( status ))

    return status;

 


  RtlInitUnicodeString( &linkString, L"\\DosDevices\\WssHookInt2e" );


  status = IoCreateSymbolicLink (&linkString, &nameString);


  if (!NT_SUCCESS( status ))

  {

    IoDeleteDevice (DriverObject->DeviceObject);

    return status;

  } 

 


  for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)  {


    DriverObject->MajorFunction[i] = MydrvDispatch;

  }


  DriverObject->DriverUnload = DriverUnload;


  ProcessNameOffset = GetProcessNameOffset();

  InstallNewInt2e();


return STATUS_SUCCESS;

}




//处理设备对象操作


static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

{

  Irp->IoStatus.Status = STATUS_SUCCESS;

  Irp->IoStatus.Information = 0L;

  IoCompleteRequest( Irp, 0 );

  return Irp->IoStatus.Status;

 

}




VOID DriverUnload (IN PDRIVER_OBJECT  pDriverObject)

{

  UNICODE_STRING nameString;


  UninstallNewInt2e();

  RtlInitUnicodeString( &nameString, L"\\DosDevices\\WssHookInt2e" ); 

  IoDeleteSymbolicLink(&nameString);

  IoDeleteDevice(pDriverObject->DeviceObject);


  return;

}




ULONG GetProcessNameOffset()

{

    PEPROCESS curproc;

    int i;

   

    curproc = PsGetCurrentProcess();


    //

    // Scan for 12KB, hopping the KPEB never grows that big!

    //

    for( i = 0; i < 3*PAGE_SIZE; i++ ) {


      if( !strncmp( SYSNAME, (PCHAR) curproc + i, strlen(SYSNAME) )) {


        return i;

      }

    }


    //

    // Name not found - oh, well

    //

    return 0;

}


VOID GetProcessName( PCHAR Name )

{


    PEPROCESS curproc;

    char *nameptr;

    ULONG i;


    if( ProcessNameOffset ) {


      curproc = PsGetCurrentProcess();

      nameptr = (PCHAR) curproc + ProcessNameOffset;

      strncpy( Name, nameptr, 16 );


    } else {


      strcpy( Name, "???");

    }

}



3、 HOOK PE 方法

  这种方法对于拦截、分析其他内核驱动的函数调用来说用的比较多。原理

是根据替换 PE 格式导出表中的相应函数来实现的。此方法中需要用到一些小

技巧。如内核模式并没有直接提供类似应用层的 GetModuleHandl()、GetProcAddress() 等函数来获得模块的地址。那么我们就需要自己来编写,这

里用到了一个未公开的函数与结构。ZwQuerySystemInformation 与 SYSTEM_MODULE_INFORMATION 来实现得到模块的基地址。这样我们就可以根据

PE 格式来枚举导出表中的函数来替换了。但这又引出了一个问题,那就是从

WINDOWS 2000 后内核数据的页属性都是只读的,不能更改。内核模式也没有

提供类似应用层的 VirtualProtectEx() 等函数来修改页面属性。那么也需要

我们自己来编写。因为我们是在内核模式所以我们可以通过修改 cr0 寄存器的

的写保护位来达到我们的目的。这样我们所期望的拦截内核模式函数的功能便

得以实现。此方法需要你对 PE 格式有一定的基础。下面的程序演示了这一过程。




/*****************************************************************

文件名    : WssHookPE.c

描述    : 拦截内核函数

作者    : sinister

最后修改日期 : 2002-11-02

*****************************************************************/


#include "ntddk.h"

#include "windef.h"



typedef enum _SYSTEM_INFORMATION_CLASS {

  SystemBasicInformation,

  SystemProcessorInformation,

  SystemPerformanceInformation,

  SystemTimeOfDayInformation,

  SystemNotImplemented1,

  SystemProcessesAndThreadsInformation,

  SystemCallCounts,

  SystemConfigurationInformation,

  SystemProcessorTimes,

  SystemGlobalFlag,

  SystemNotImplemented2,

  SystemModuleInformation,

  SystemLockInformation,

  SystemNotImplemented3,

  SystemNotImplemented4,

  SystemNotImplemented5,

  SystemHandleInformation,

  SystemObjectInformation,

  SystemPagefileInformation,

  SystemInstructionEmulationCounts,

  SystemInvalidInfoClass1,

  SystemCacheInformation,

  SystemPoolTagInformation,

  SystemProcessorStatistics,

  SystemDpcInformation,

  SystemNotImplemented6,

  SystemLoadImage,

  SystemUnloadImage,

  SystemTimeAdjustment,

  SystemNotImplemented7,

  SystemNotImplemented8,

  SystemNotImplemented9,

  SystemCrashDumpInformation,

  SystemExceptionInformation,

  SystemCrashDumpStateInformation,

  SystemKernelDebuggerInformation,

  SystemContextSwitchInformation,

  SystemRegistryQuotaInformation,

  SystemLoadAndCallImage,

  SystemPrioritySeparation,

  SystemNotImplemented10,

  SystemNotImplemented11,

  SystemInvalidInfoClass2,

  SystemInvalidInfoClass3,

  SystemTimeZoneInformation,

  SystemLookasideInformation,

  SystemSetTimeSlipEvent,

  SystemCreateSession,

  SystemDeleteSession,

  SystemInvalidInfoClass4,

  SystemRangeStartInformation,

  SystemVerifierInformation,

  SystemAddVerifier,

  SystemSessionProcessesInformation

} SYSTEM_INFORMATION_CLASS;



typedef struct tagSYSTEM_MODULE_INFORMATION {

  ULONG Reserved[2];

  PVOID Base;

  ULONG Size;

  ULONG Flags;

  USHORT Index;

  USHORT Unknown;

  USHORT LoadCount;

  USHORT ModuleNameOffset;

  CHAR ImageName[256];

} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;


#define IMAGE_DOS_SIGNATURE    0x5A4D  // MZ

#define IMAGE_NT_SIGNATURE  0x50450000 // PE00

#define IMAGE_NT_SIGNATURE1    0x00004550  // 00EP


typedef struct _IMAGE_DOS_HEADER {  // DOS .EXE header

  WORD  e_magic;          // Magic number

  WORD  e_cblp;          // Bytes on last page of file

  WORD  e_cp;            // Pages in file

  WORD  e_crlc;          // Relocations

  WORD  e_cparhdr;          // Size of header in paragraphs

  WORD  e_minalloc;        // Minimum extra paragraphs needed

  WORD  e_maxalloc;        // Maximum extra paragraphs needed

  WORD  e_ss;            // Initial (relative) SS value

  WORD  e_sp;            // Initial SP value

  WORD  e_csum;          // Checksum

  WORD  e_ip;            // Initial IP value

  WORD  e_cs;            // Initial (relative) CS value

  WORD  e_lfarlc;          // File address of relocation table

  WORD  e_ovno;          // Overlay number

  WORD  e_res[4];          // Reserved words

  WORD  e_oemid;          // OEM identifier (for e_oeminfo)

  WORD  e_oeminfo;          // OEM information; e_oemid specific

  WORD  e_res2[10];        // Reserved words

  LONG  e_lfanew;          // File address of new exe header

} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;



typedef struct _IMAGE_FILE_HEADER {

  WORD  Machine;

  WORD  NumberOfSections;

  DWORD  TimeDateStamp;

  DWORD  PointerToSymbolTable;

  DWORD  NumberOfSymbols;

  WORD  SizeOfOptionalHeader;

  WORD  Characteristics;

} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;


typedef struct _IMAGE_DATA_DIRECTORY {

  DWORD  VirtualAddress;

  DWORD  Size;

} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;


#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES  16


//

// Optional header format.

//


typedef struct _IMAGE_OPTIONAL_HEADER {

  //

  // Standard fields.

  //


  WORD  Magic;

  BYTE  MajorLinkerVersion;

  BYTE  MinorLinkerVersion;

  DWORD  SizeOfCode;

  DWORD  SizeOfInitializedData;

  DWORD  SizeOfUninitializedData;

  DWORD  AddressOfEntryPoint;

  DWORD  BaseOfCode;

  DWORD  BaseOfData;


  //

  // NT additional fields.

  //


  DWORD  ImageBase;

  DWORD  SectionAlignment;

  DWORD  FileAlignment;

  WORD  MajorOperatingSystemVersion;

  WORD  MinorOperatingSystemVersion;

  WORD  MajorImageVersion;

  WORD  MinorImageVersion;

  WORD  MajorSubsystemVersion;

  WORD  MinorSubsystemVersion;

  DWORD  Win32VersionValue;

  DWORD  SizeOfImage;

  DWORD  SizeOfHeaders;

  DWORD  CheckSum;

  WORD  Subsystem;

  WORD  DllCharacteristics;

  DWORD  SizeOfStackReserve;

  DWORD  SizeOfStackCommit;

  DWORD  SizeOfHeapReserve;

  DWORD  SizeOfHeapCommit;

  DWORD  LoaderFlags;

  DWORD  NumberOfRvaAndSizes;

  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];

} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;


typedef struct _IMAGE_NT_HEADERS {

  DWORD Signature;

  IMAGE_FILE_HEADER FileHeader;

  IMAGE_OPTIONAL_HEADER32 OptionalHeader;

} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;


typedef IMAGE_NT_HEADERS32        IMAGE_NT_HEADERS;

typedef PIMAGE_NT_HEADERS32        PIMAGE_NT_HEADERS;


//

// Section header format.

//


#define IMAGE_SIZEOF_SHORT_NAME      8


typedef struct _IMAGE_SECTION_HEADER {

  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];

  union {

      DWORD  PhysicalAddress;

      DWORD  VirtualSize;

  } Misc;

  DWORD  VirtualAddress;

  DWORD  SizeOfRawData;

  DWORD  PointerToRawData;

  DWORD  PointerToRelocations;

  DWORD  PointerToLinenumbers;

  WORD  NumberOfRelocations;

  WORD  NumberOfLinenumbers;

  DWORD  Characteristics;

} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;


#define IMAGE_SIZEOF_SECTION_HEADER    40

//

// Export Format

//


typedef struct _IMAGE_EXPORT_DIRECTORY {

  DWORD  Characteristics;

  DWORD  TimeDateStamp;

  WORD  MajorVersion;

  WORD  MinorVersion;

  DWORD  Name;

  DWORD  Base;

  DWORD  NumberOfFunctions;

  DWORD  NumberOfNames;

  DWORD  AddressOfFunctions;  // RVA from base of image

  DWORD  AddressOfNames;    // RVA from base of image

  DWORD  AddressOfNameOrdinals; // RVA from base of image

} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;


#define BASEADDRLEN 10


NTSYSAPI

NTSTATUS

NTAPI

ZwQuerySystemInformation(

  IN SYSTEM_INFORMATION_CLASS SystemInformationClass,

  IN OUT PVOID SystemInformation,

  IN ULONG SystemInformationLength,

  OUT PULONG ReturnLength OPTIONAL

  );



typedef NTSTATUS (* ZWCREATEFILE)(

OUT PHANDLE FileHandle,

IN ACCESS_MASK DesiredAccess,

IN POBJECT_ATTRIBUTES ObjectAttributes,

OUT PIO_STATUS_BLOCK IoStatusBlock,

IN PLARGE_INTEGER AllocationSize OPTIONAL,

IN ULONG FileAttributes,

IN ULONG ShareAccess,

IN ULONG CreateDisposition,

IN ULONG CreateOptions,

IN PVOID EaBuffer OPTIONAL,

IN ULONG EaLength

);


ZWCREATEFILE  OldZwCreateFile;


static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);

VOID DisableWriteProtect( PULONG pOldAttr);

VOID EnableWriteProtect( ULONG ulOldAttr );

FARPROC HookFunction(  PCHAR pModuleBase, PCHAR pHookName, FARPROC pHookFunc );


NTSTATUS

HookNtCreateFile(

OUT PHANDLE FileHandle,

IN ACCESS_MASK DesiredAccess,

IN POBJECT_ATTRIBUTES ObjectAttributes,

OUT PIO_STATUS_BLOCK IoStatusBlock,

IN PLARGE_INTEGER AllocationSize OPTIONAL,

IN ULONG FileAttributes,

IN ULONG ShareAccess,

IN ULONG CreateDisposition,

IN ULONG CreateOptions,

IN PVOID EaBuffer OPTIONAL,

IN ULONG EaLength

);




PCHAR MyGetModuleBaseAddress( PCHAR pModuleName )

{

  PSYSTEM_MODULE_INFORMATION  pSysModule; 


  ULONG      uReturn;

  ULONG      uCount;

  PCHAR      pBuffer = NULL;

  PCHAR      pName  = NULL;

  NTSTATUS    status;

  UINT      ui;


  CHAR      szBuffer[BASEADDRLEN];

  PCHAR      pBaseAddress;

 

  status = ZwQuerySystemInformation( SystemModuleInformation, szBuffer, BASEADDRLEN, &uReturn );


  pBuffer = ( PCHAR )ExAllocatePool( NonPagedPool, uReturn );


  if ( pBuffer )

  {

    status = ZwQuerySystemInformation( SystemModuleInformation, pBuffer, uReturn, &uReturn );


    if( status == STATUS_SUCCESS )

    {

      uCount = ( ULONG )*( ( ULONG * )pBuffer );

      pSysModule = ( PSYSTEM_MODULE_INFORMATION )( pBuffer + sizeof( ULONG ) );


      for ( ui = 0; ui < uCount; ui++ )

      {

        pName = MyStrchr( pSysModule->ImageName, '\\' );


        if ( !pName )

        {

          pName = pSysModule->ImageName;

        }


        else {

          pName++;

        }


        if( !_stricmp( pName, pModuleName ) )

        {

          pBaseAddress = ( PCHAR )pSysModule->Base;

          ExFreePool( pBuffer );

          return pBaseAddress;

        }


        pSysModule ++;

      }

    }


    ExFreePool( pBuffer );

  }


  return NULL;

}



FARPROC HookFunction( PCHAR pModuleBase, PCHAR HookFunName, FARPROC HookFun )

{

  PIMAGE_DOS_HEADER    pDosHdr;

  PIMAGE_NT_HEADERS    pNtHdr;

  PIMAGE_SECTION_HEADER  pSecHdr;

  PIMAGE_EXPORT_DIRECTORY pExtDir;


  UINT          ui,uj;

  PCHAR          FunName;

  DWORD          *dwAddrName;

  DWORD          *dwAddrFun;

  FARPROC          pOldFun;

  ULONG          uAttrib;



  pDosHdr = ( PIMAGE_DOS_HEADER )pModuleBase;


  if ( IMAGE_DOS_SIGNATURE == pDosHdr->e_magic )

  {

    pNtHdr = ( PIMAGE_NT_HEADERS )( pModuleBase + pDosHdr->e_lfanew );


    if( IMAGE_NT_SIGNATURE == pNtHdr->Signature ||  IMAGE_NT_SIGNATURE1 == pNtHdr->Signature )

    {

      pSecHdr = ( PIMAGE_SECTION_HEADER )( pModuleBase + pDosHdr->e_lfanew + sizeof( IMAGE_NT_HEADERS ) );


      for ( ui = 0; ui < (UINT)pNtHdr->FileHeader.NumberOfSections; ui++ )

      {

        if ( !strcmp( pSecHdr->Name, ".edata" ) )

        {       

          pExtDir = ( PIMAGE_EXPORT_DIRECTORY )( pModuleBase + pSecHdr->VirtualAddress );

          dwAddrName = ( PDWORD )(pModuleBase + pExtDir->AddressOfNames );

          dwAddrFun = ( PDWORD )(pModuleBase + pExtDir->AddressOfFunctions );


          for ( uj = 0; uj < (UINT)pExtDir->NumberOfFunctions; uj++ )

          {

            FunName = pModuleBase + *dwAddrName;


            if( !strcmp( FunName, HookFunName ) )

            {

              DbgPrint(" HOOK %s()\n",FunName);

              DisableWriteProtect( &uAttrib );

              pOldFun = ( FARPROC )( pModuleBase + *dwAddrFun );

              *dwAddrFun = ( PCHAR )HookFun - pModuleBase;

              EnableWriteProtect( uAttrib );

              return pOldFun;

            }


          dwAddrName ++;

          dwAddrFun ++;

          }

        }


        pSecHdr++;

      }

    }

  }


  return NULL;

}



// 驱动入口

NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )

{

 

  UNICODE_STRING nameString, linkString;

  PDEVICE_OBJECT deviceObject;

  NTSTATUS    status;

  HANDLE    hHandle;

  PCHAR      pModuleAddress;

  int        i;

 


  //卸载驱动

  DriverObject->DriverUnload = DriverUnload;


  //建立设备

  RtlInitUnicodeString( &nameString, L"\\Device\\WssHookPE" );

 

  status = IoCreateDevice( DriverObject,

              0,

              &nameString,

              FILE_DEVICE_UNKNOWN,

              0,

              TRUE,

              &deviceObject

              );

             


  if (!NT_SUCCESS( status ))

    return status;

 


  RtlInitUnicodeString( &linkString, L"\\DosDevices\\WssHookPE" );


  status = IoCreateSymbolicLink (&linkString, &nameString);


  if (!NT_SUCCESS( status ))

  {

    IoDeleteDevice (DriverObject->DeviceObject);

    return status;

  } 

 

  pModuleAddress = MyGetModuleBaseAddress("ntoskrnl.exe");

  if ( pModuleAddress == NULL)

  {

    DbgPrint(" MyGetModuleBaseAddress()\n");

    return 0;

  }


  OldZwCreateFile = (ZWCREATEFILE)HookFunction( pModuleAddress, "ZwCreateFile",(ZWCREATEFILE)HookNtCreateFile);

  if ( OldZwCreateFile == NULL)

  {

    DbgPrint(" HOOK FAILED\n");

    return 0;

  }


  DbgPrint("HOOK SUCCEED\n");


  for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)  {


    DriverObject->MajorFunction[i] = MydrvDispatch;

  }


  DriverObject->DriverUnload = DriverUnload;

 

return STATUS_SUCCESS;

}




//处理设备对象操作


static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

{

  Irp->IoStatus.Status = STATUS_SUCCESS;

  Irp->IoStatus.Information = 0L;

  IoCompleteRequest( Irp, 0 );

  return Irp->IoStatus.Status;

 

}




VOID DriverUnload (IN PDRIVER_OBJECT  pDriverObject)

{

  UNICODE_STRING nameString;

  PCHAR      pModuleAddress;


  pModuleAddress = MyGetModuleBaseAddress("ntoskrnl.exe");

  if ( pModuleAddress == NULL)

  {

    DbgPrint("MyGetModuleBaseAddress()\n");

    return ;

  }


  OldZwCreateFile = (ZWCREATEFILE)HookFunction( pModuleAddress, "ZwCreateFile",(ZWCREATEFILE)OldZwCreateFile);

  if ( OldZwCreateFile == NULL)

  {

    DbgPrint(" UNHOOK FAILED!\n");

    return ;

  }


  DbgPrint("UNHOOK SUCCEED\n");


  RtlInitUnicodeString( &nameString, L"\\DosDevices\\WssHookPE" ); 

  IoDeleteSymbolicLink(&nameString);

  IoDeleteDevice(pDriverObject->DeviceObject);


  return;

}


NTSTATUS

HookNtCreateFile(

OUT PHANDLE FileHandle,

IN ACCESS_MASK DesiredAccess,

IN POBJECT_ATTRIBUTES ObjectAttributes,

OUT PIO_STATUS_BLOCK IoStatusBlock,

IN PLARGE_INTEGER AllocationSize OPTIONAL,

IN ULONG FileAttributes,

IN ULONG ShareAccess,

IN ULONG CreateDisposition,

IN ULONG CreateOptions,

IN PVOID EaBuffer OPTIONAL,

IN ULONG EaLength

)

{

  NTSTATUS  status;


  DbgPrint("Hook ZwCreateFile()\n");


  status = ((ZWCREATEFILE)(OldZwCreateFile))(

        FileHandle,

        DesiredAccess,

        ObjectAttributes,

        IoStatusBlock,

        AllocationSize,

        FileAttributes,

        ShareAccess,

        CreateDisposition,

        CreateOptions,

        EaBuffer,

        EaLength

      );


  return status;

}



VOID DisableWriteProtect( PULONG pOldAttr)

{


  ULONG uAttr;


  _asm

  {

    push eax;

    mov eax, cr0;

    mov uAttr, eax;

    and eax, 0FFFEFFFFh; // CR0 16 BIT = 0

    mov cr0, eax;

    pop eax;

  };


  *pOldAttr = uAttr; //保存原有的 CRO 属性


}


VOID EnableWriteProtect( ULONG uOldAttr )

{


_asm

{

    push eax;

    mov eax, uOldAttr; //恢复原有 CR0 属性

    mov cr0, eax;

    pop eax;

};


}


B20层 发表时间: 04-01-29 23:14

回复: ricky [ricky]   版主   登录
多谢,受益非浅

B21层 发表时间: 04-01-30 09:07

回复: tabris17 [tabris17]   论坛用户   登录
第二种方法不知在xp下有效么?

xp好像不使用int 2e进行ring0切换

B22层 发表时间: 04-01-30 15:39

回复: tabris17 [tabris17]   论坛用户   登录
好东西啊,一个NDIS HOOK和一个TDI FILTER的源代码 http://www.20cn.net/~tabris17/temp/ndis_tdi.rar

我还没来得及看

B23层 发表时间: 04-01-30 15:42

回复: sinister [sinister]   论坛用户   登录
XP 下用 sysentry 来切换到 ring 0 下。但 sysentry 只能在应用层调用,并不能象
以前 NT/2K 下的 int 2e。 在内核调用 NTOSKRNL 导出的 NATIVE API 时需要调用
int 2e 重入内核,正是因为 sysentry 这种限制,所以 XP 保留了 int 2e 调用。

说到了 NATIVE API 我顺便提一句。很多人认为 NATIVE API 是这样的流程,
NTDLL -> NtXX -> NTOSKRNL -> ZwXXX。 这是个错误的理解。注意,NATIVE API
有两套。一个是 ring 3 NTDLL 导出的,一个是由 ring 0 NTOSKRNL 导出的。
ring 3 的 NTDLL 导出了全部 NATIVE API。NTOSKRNL 则没有。两套 NATIVE API
都分别有各自的 Nt/Zw 开头的函数,当在 ring 3 调用的时由 int 2e / sysentry
切换到 ring 0。当在内核调用的时,通过 int 2e 重入内核。注意区别在于,当
ring 3 调用时需要很多检查。而内核调用时则无须检查。再有就是 ZwXXX 最终调用
NtXXX。 NtXXX 才是真正的执行代码。


BTW: 那两个代码我下来看了看,真的很不习惯他的代码风格。

B24层 发表时间: 04-01-31 00:33

回复: tabris17 [tabris17]   论坛用户   登录
引用:
再有就是 ZwXXX 最终调用
NtXXX。 NtXXX 才是真正的执行代码。



明白了


BTW,是啊,那代码也没个文档,看起来真累


[此贴被 四不象(tabris17) 在 01月31日14时44分 编辑过]

B25层 发表时间: 04-01-31 14:42

论坛: 原创软件

20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon

粤ICP备05087286号