首页
社区
课程
招聘
[分享]《另类挂钩-RING3数据包监视》应用 -- 网址过滤+重定向的Delphi实现
发表于: 2009-11-8 00:41 34619

[分享]《另类挂钩-RING3数据包监视》应用 -- 网址过滤+重定向的Delphi实现

2009-11-8 00:41
34619
关于qihoocom大侠的 另类挂钩-RING3数据包监视 相信很多人都看过了。不过明明不是我写的代码,这已经是第四个人问我这个代码应该怎么改了,而且基本都是类似的要求——URL过滤或者拦截指定数据包。
也许是太简单了,没见MJ在博客和论坛谈论这个代码的具体应用,不过不得不承认Hook NtDeviceIoControlFile应用还是相当广泛的。
比如我这里演示的两个功能:过滤URL页面劫持(重定向)

1. 过滤包含指定URL的访问最简单,匹配到这样的 GET/POST请求后,直接返回错误就OK了。
2. 页面劫持就是通过 HTTP 301 消息,伪造页面被移走的假象来欺骗浏览器,当然这个要实现的话,需要浏览器支持重定向功能。

代码依然在原来的基础上修改来的,主要修改如下:
  // 过滤参数
  Block_Filter = 'google.com';            // 阻止所有对 google.com 的请求
  // 重定向参数
  URL_Filter = 'bbs.pediy.com';           // 要重定向的URL (小写)
  Redirect_String: AnsiString =           // 重定向字符串(使用 HTTP 301)
      'HTTP/1.1 301 Moved Permanently'#13#10'Location: ' +
      'http://www.baidu.com/search/error.html' +
      #0;

[COLOR="Silver"]function NewNtDeviceIoControlFile():[/COLOR]
    case IoControlCode of
      AFD_SEND:
        if ( LookupSendPacket(Buffer, Len) ) then
        begin
          // 输出包内容
          OutputDebugString(PChar(Format('[HTTP Send] Handle = %0.8X, Length = %d', [FileHandle, Len])));
          OutputDebugString(PChar(Format('%s', [StrPas(Buffer)])));
          tmpStr := LowerCase(String(StrPas(Buffer)));

        {  网址过滤实现  }
          if (Pos(Block_Filter, tmpStr) > 0) then
          begin
            OutputDebugString(PChar(Format('>>> "%s" was blocked.', [Block_Filter])));
            Result := $C0000001;      // 返回失败让系统丢掉这个包
          end;

        {  重定向实现  }
          // 查找发送的数据中是否包含 URL_Filter 中的字符串
          if (Pos(URL_Filter, tmpStr) > 0) then
          begin
            // 匹配到 URL_Filter,将Handle加入 SessionList
            tmpStr := IntToHex(FileHandle, 8);
            if (SessionList.IndexOf(tmpStr) = -1) then  // 如果没有这个Handle
              SessionList.Add(tmpStr);
          end;
        end;
      AFD_RECV:
        if ( LookupRecvPacket(Buffer, Len) ) then
        begin
          // 输出包内容
          OutputDebugString(PChar(Format('[HTTP Recv] Handle = %0.8X, Length = %d', [FileHandle, Len])));
          OutputDebugString(PChar(Format('%s', [StrPas(Buffer)])));

        {  重定向实现  }
          // 在SessionList查找Handle,找到则进行重定向
          tmpStr := IntToHex(FileHandle, 8);
          Index := SessionList.IndexOf(tmpStr);
          if (Index >= 0) then
          begin
            SessionList.Delete(Index);
            // 修改缓冲区内容为重定向字符串,
            CopyMemory(Buffer, @Redirect_String[1], Length(Redirect_String));
            OutputDebugString(PChar(Format('>>> Redirect "%s" to a new page.', [URL_Filter])));
          end;
        end;
    end;

为了方便实现,利用了 TStringList.IndefOf() 查找Socket句柄,为了效率建议自己实现查询。另外很多地方没有完善的判断,比如直接
  CopyMemory(Buffer, @Redirect_String[1], Length(Redirect_String));
可能导致溢出,需要首先 if (Len > Length(Redirect_String)) then 再去修改缓冲区。

完整工程文件见附件。注入dll到浏览器后就能看到效果,这段代码会阻止所有对 google.com 的访问,并且将看雪论坛的请求全部重定向到百度的那个地址上

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (48)
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
2
你知道为啥问你吗,因为问我的我一般都不理 好吧 哈哈
2009-11-8 01:32
0
雪    币: 296
活跃值: (89)
能力值: ( LV15,RANK:340 )
在线值:
发帖
回帖
粉丝
3
果然...
不过还是感谢分享这么好的技术,这里挂钩后处理数据包相当方便
2009-11-8 08:23
0
雪    币: 54
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
[QUOTE=木桩;709070]为了方便实现,利用了 TStringList.IndefOf() 查找Socket句柄,为了效率建议自己实现查询。另外很多地方没有完善的判断,比如直接
  CopyMemory(Buffer, @Redirect_String[1], Length(Redirect_String));
可能导致溢出,需要首先 if (Len > Length(Redirect_String)) then 再去修改缓冲区。
...[/QUOTE]

if (Len > Length(Redirect_String)) then  //验证 InputBuffer的Len 大于 重新向的Len
        begin
                SessionList.Delete(Index);
                // 修改缓冲区内容为重定向字符串,    
                CopyMemory(Buffer, @Redirect_String[1], Length(Redirect_String));
                OutputDebugString(PChar(Format('>>> Redirect "%s" to a new page.', [URL_Filter])));
        end;

对了、木桩 用此方案是否可以 不用考虑Tcp的包长问题了?

另外:此方案中替换内存中的URL就难了~ 因为很难得到现有的URL

不知是不是把Host和GET 分别取回来,在重新构建成一个http://请求字符串~ ?
2009-11-9 09:24
0
雪    币: 54
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
2009-11-9 09:33
0
雪    币: 171
活跃值: (279)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
在MJ牛的基础上更改一下,能做的事情还是很多的哈。。。
2009-11-9 13:21
0
雪    币: 171
活跃值: (279)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
MJ牛。。。。
2009-11-9 13:34
0
雪    币: 54
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
  如何取到一个完整的url请求、比如:{http://bbs.pediy.com/newreply.php?do=newreply&p=709126}

  我将缓冲区打印出来、表现为↓

截取部份数据

00008815  28.42535210  [4048] GET /js/bdsug.js?v=1.0.1.0 HTTP/1.1   
00008816  28.42535210  [4048] Accept: */*   
00008817  28.42535210  [4048] Referer: http://www.baidu.com/   
00008818  28.42535210  [4048] Accept-Language: zh-cn   
00008819  28.42535210  [4048] User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727; CIBA; TheWorld)   
00008820  28.42535210  [4048] UA-CPU: x86   
00008821  28.42535210  [4048] Accept-Encoding: gzip, deflate   
00008822  28.42535210  [4048] If-Modified-Since: Mon, 19 Jan 2009 13:18:00 GMT   
00008823  28.42535210  [4048] If-None-Match: "1599-49747d88"   
00008824  28.42535210  [4048] Host: www.baidu.com   
00008825  28.42535210  [4048] Connection: Keep-Alive   
00008826  28.42535210  [4048] Cookie: BAIDUID=F07CEBAE4F4B5A6DE8A3D73BDA7CBB34:FG=1;...
2009-11-9 14:30
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
9
按host+URL拼就可以了。

另外,楼主用301,问题多多 呵呵。例如你可以试试对迅雷用这个,会崩溃
2009-11-9 15:05
0
雪    币: 296
活跃值: (89)
能力值: ( LV15,RANK:340 )
在线值:
发帖
回帖
粉丝
10
没试过迅雷,不过我倒没感觉用301的永久重定向有什么特别突出的问题,怎么处理301/302这个请求就看各客户端的了,至少我测试的IE6/7和Google的Chrome都能正常工作。另外 HTTP/1.1规范(RFC 2616) 提到还有303/307都可以干类似的事情。不过毕竟只是规范而已,具体客户端想不想遵守那是他们各自的事情,而且这个301请求确实太单调了点(只有个必要的Location参数)。
这个只是演示下怎么处理数据包,不跨站的话还是直接改GET/POST参数更稳妥些。
2009-11-9 20:52
0
雪    币: 54
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
00008815  28.42535210  [4048] GET /js/bdsug.js?v=1.0.1.0 HTTP/1.1   
00008816  28.42535210  [4048] Accept: */*   
00008817  28.42535210  [4048] Referer: http://www.baidu.com/   
00008818  28.42535210  [4048] Accept-Language: zh-cn   
00008819  28.42535210  [4048] User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727; CIBA; TheWorld)   
00008820  28.42535210  [4048] UA-CPU: x86   
00008821  28.42535210  [4048] Accept-Encoding: gzip, deflate   
00008822  28.42535210  [4048] If-Modified-Since: Mon, 19 Jan 2009 13:18:00 GMT   
00008823  28.42535210  [4048] If-None-Match: "1599-49747d88"   
00008824  28.42535210  [4048] Host: www.baidu.com   
00008825  28.42535210  [4048] Connection: Keep-Alive   
00008826  28.42535210  [4048] Cookie: BAIDUID=F07CEBAE4F4B5A6DE8A3D73BDA7CBB34:FG=1;...

GET的值很容易拿到、不过HOST的值好像没有那么容易了!
2009-11-11 13:07
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
12
[QUOTE=西南;710640]00008815  28.42535210  [4048] GET /js/bdsug.js?v=1.0.1.0 HTTP/1.1   
00008816  28.42535210  [4048] Accept: */*   
00008817  28.42535210  [4048] Refe...[/QUOTE]

00008824  28.42535210  [4048] Host: www.baidu.com   

===
眼力太差
2009-11-11 14:16
0
雪    币: 54
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
[QUOTE=qihoocom;710670]00008824  28.42535210  [4048] Host: www.baidu.com   

===
眼力太差[/QUOTE]

s:='GET / /asp?id=8 HTTP/1.1';

拿GET可以用(GetStr(s,'GET /','HTTP/1.1'))
function GetStr(StrSource,StrBegin,StrEnd:String):String;
var
in_star,in_end:integer;
begin
  in_star:=AnsiPos(StrBegin,StrSource)+length(StrBegin);
  in_end:=AnsiPos(StrEnd,StrSource);
  result:=copy(StrSource,in_star,in_end-in_star);
{
  函数里的AnsiPos和copy,都是系统定义的,可从delphi的帮助文件里找到相关说明,
  function AnsiPos(const Substr, S: string): Integer
  返回Substr在S中第一次出现的位置。
  function copy(strsource,in_star,in_end-in_star): string;
  返回字符串strsource中,从in_star(整型数据)开始到in_end-in_star(整型数据)结束的字符串。
}
end;

HOST:~~~ ???
2009-11-11 14:25
0
雪    币: 796
活跃值: (370)
能力值: ( LV9,RANK:380 )
在线值:
发帖
回帖
粉丝
14
delphi看不懂。。。C代码就好了。。
2009-11-11 16:57
0
雪    币: 296
活跃值: (89)
能力值: ( LV15,RANK:340 )
在线值:
发帖
回帖
粉丝
15
Pos()定位啊,1楼代码里不是用过这个函数吗?
Pos('Host: ', Buffer) 就是Host: 开始的位置,向后一直取到回车换行符(#13#10)就行了
strFilter := 'Host: ';
i := Pos(strFilter, Buffer);
while (Buffer[i] <> #13) do
begin
  // 这里将 Buffer[i] 的字符复制出来
  Inc(i);
end;
2009-11-11 20:54
0
雪    币: 796
活跃值: (370)
能力值: ( LV9,RANK:380 )
在线值:
发帖
回帖
粉丝
16
木桩兄能否用C写一个呢,delphi的看不懂。。
2009-11-13 23:32
0
雪    币: 296
活跃值: (89)
能力值: ( LV15,RANK:340 )
在线值:
发帖
回帖
粉丝
17
原理都讲了,C和Delphi的区别不大。Delphi的Pos()函数相当于C的strstr(),也就是在Buffer中匹配要过滤的URL,如果找到则进行处理。你要是看不懂就直接替换这两个函数好了。处理的位置都一样,就是在相面两个if中加入判断:
if (IoControlCode == AFD_SEND)
{
  if (LookupSendPacket(Buffer , Len))
  {
    //输出包内容
    //这里输出调试信息,可以用DbgView查看,如果有UI可以做成SendMessage形式~
    OutputDebugString("SendPacket!\n");
    OutputDebugString((char*)Buffer);
[COLOR="Red"]    // 在这里加入对发送的 GET/POST 判断,比如匹配到对google.com的访问,想过滤则直接return 0xC0000001[/COLOR]
  }
}
else
{
  if (LookupRecvPacket(Buffer , Len))
  {
    OutputDebugString("RecvPacket!\n");
    OutputDebugString((char*)Buffer);
  }
}


总之就是匹配、修改Buffer的内容而已,之所以不用C是因为TStringList的查询用得比较顺手,C中想实现重定向需要自己实现简单的Handle查询算法。
2009-11-14 21:27
0
雪    币: 8865
活跃值: (2379)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
18
要具体改post里的数据才行啊~
现在好多猥琐的东西,要改具体的Post数据
所以还是强大的connect代理变态~
2009-11-15 19:00
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
很深奥。为什么在win7下无效呢?
2009-11-15 23:35
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
20
connect代理变态?connect代理算个P啊,今天看到个老外的MINIPORT驱动,实打实的代码写了1MB,数据包全部内核处理掉了,支持各种功能,包括在内核画界面,命令行等等~膜拜啊~
2009-11-16 01:50
0
雪    币: 284
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
那老外估计是职业杀手~
国外的环境不一样~
2009-11-16 09:46
0
雪    币: 796
活跃值: (370)
能力值: ( LV9,RANK:380 )
在线值:
发帖
回帖
粉丝
22
我想要的就是这个代码呀
2009-11-16 10:54
0
雪    币: 296
活跃值: (89)
能力值: ( LV15,RANK:340 )
在线值:
发帖
回帖
粉丝
23
不方便。要是不求效率,弄个数组循环着比较不就解决了,反正存的是DWORD...
2009-11-16 20:42
0
雪    币: 54
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
木桩↓

你的方案必须注入到指定的浏览器运行时才能生效!
2009-11-17 21:52
0
雪    币: 250
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
25
我想,你该普及下什么是hook了...
2009-11-17 21:57
0
游客
登录 | 注册 方可回帖
返回
//