当前位置:编程学习 > C#/ASP.NET >>

C#2008中 成功HOOK了send发送封包,但同样方法HOOK了recv拦收封包打开网页乱码

--------------------编程问答-------------------- 难道就没人会??? --------------------编程问答--------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Collections;
using System.Runtime.InteropServices;
using api_hook;


namespace api_hook_封包学习
{
  public partial class Form1 : Form
  {
  [DllImport("ws2_32.dll")]
  static extern int send(int s, byte[] buf, int len, int flag);

  [DllImport("ws2_32.dll")]
  static extern int recv(int s, byte[] buf, int len, int flag);

  APIHOOK send_Hook = new APIHOOK();
  delegate int sendCallback(int s, IntPtr buf, int len, int flag);
  delegate int recvCallback(int s, IntPtr buf, int len, int flag);
  public Form1()
  {
  InitializeComponent();
  }

  private void Form1_Load(object sender, EventArgs e)
  {
  //关键代码在这里,如果我用这句拦截发送的包完全正确
  //send_Hook.Install("ws2_32.dll", "send", Marshal.GetFunctionPointerForDelegate(new sendCallback(sendProc)));
//但我用同样的方法拦截接收的包也收到包,但时打开网页就乱码,请高人指点
  send_Hook.Install("ws2_32.dll", "recv", Marshal.GetFunctionPointerForDelegate(new recvCallback(toProc)));
  }

  int sendProc(int s, IntPtr buf, int len, int flag)
  {
  byte[] buffer = new byte[len];
  Marshal.Copy(buf, buffer, 0, len); //读封包数据,读取后可进行条件修改,拦截,转发等,记得处理后调用发送   
  send_Hook.Suspend(); //暂停拦截,转交系统调用   
  int ret = send(s, buffer, len, flag); //发送数据,此处可进行拦截
  send_Hook.Continue(); //恢复HOOK   
  return ret;
  }
  int toProc(int s, IntPtr buf, int len, int flag)
  {
  byte[] buffer = new byte[len];
  Marshal.Copy(buf, buffer, 0, len); //读封包数据  
  send_Hook.Suspend(); //暂停拦截,转交系统调用   
  int ret = recv(s, buffer, len, flag); //发送数据,此处可对包进行处理操作
  send_Hook.Continue(); //恢复HOOK   
  return ret;
  }
  private void button1_Click(object sender, EventArgs e)//按下时加载网页进行测试数据
  {
  webBrowser1.Navigate("http://baidu.com");
  }  
  }
//APIHOOK关键代码类
  public class APIHOOK
  {
  #region Api声明
  [DllImport("Kernel32.dll", EntryPoint = "GetModuleHandleA", CharSet = CharSet.Ansi)]
  static extern IntPtr GetModuleHandle(
  string lpModuleName
  );
  [DllImport("Kernel32.dll")]
  static extern bool VirtualProtect(
  IntPtr lpAddress,
  int dwSize,
  int flNewProtect,
  ref int lpflOldProtect
  );
  [DllImport("Kernel32.dll", EntryPoint = "lstrcpynA", CharSet = CharSet.Ansi)]
  static extern IntPtr lstrcpyn(
  byte[] lpString1,
  byte[] lpString2,
  int iMaxLength
  );
  [DllImport("Kernel32.dll")]
  static extern IntPtr GetProcAddress(
  IntPtr hModule,
  string lpProcName
  );
  [DllImport("Kernel32.dll")]
  static extern bool FreeLibrary(
  IntPtr hModule
  );
  #endregion
  #region 常量定义表
  const int PAGE_EXECUTE_READWRITE = 0x40;
  #endregion
  #region 变量表
  IntPtr ProcAddress;
  int lpflOldProtect = 0;
  byte[] OldEntry = new byte[5];
  byte[] NewEntry = new byte[5];
  IntPtr OldAddress;
  #endregion
  public APIHOOK() { }
  public APIHOOK(string ModuleName, string ProcName, IntPtr lpAddress)
  {
  Install(ModuleName, ProcName, lpAddress);
  }
  public bool Install(string ModuleName, string ProcName, IntPtr lpAddress)
  {
  IntPtr hModule = GetModuleHandle(ModuleName); //取模块句柄   
  if (hModule == IntPtr.Zero) return false;
  ProcAddress = GetProcAddress(hModule, ProcName); //取入口地址   
  if (ProcAddress == IntPtr.Zero) return false;
  if (!VirtualProtect(ProcAddress, 5, PAGE_EXECUTE_READWRITE, ref lpflOldProtect)) return false; //修改内存属性   
  Marshal.Copy(ProcAddress, OldEntry, 0, 5); //读取前5字节   
  NewEntry = AddBytes(new byte[1] { 233 }, BitConverter.GetBytes((Int32)((Int32)lpAddress - (Int32)ProcAddress - 5))); //计算新入口跳转   
  Marshal.Copy(NewEntry, 0, ProcAddress, 5); //写入前5字节   
  OldEntry = AddBytes(OldEntry, new byte[5] { 233, 0, 0, 0, 0 });
  OldAddress = lstrcpyn(OldEntry, OldEntry, 0); //取变量指针   
  Marshal.Copy(BitConverter.GetBytes((double)((Int32)ProcAddress - (Int32)OldAddress - 5)), 0, (IntPtr)(OldAddress.ToInt32() + 6), 4); //保存JMP   
  FreeLibrary(hModule); //释放模块句柄   
  return true;
  }
  public void Suspend()
  {
  Marshal.Copy(OldEntry, 0, ProcAddress, 5);
  }
  public void Continue()
  {
  Marshal.Copy(NewEntry, 0, ProcAddress, 5);
  }
  public bool Uninstall()
  {
  if (ProcAddress == IntPtr.Zero) return false;
  Marshal.Copy(OldEntry, 0, ProcAddress, 5);
  ProcAddress = IntPtr.Zero;
  return true;
  }
  static byte[] AddBytes(byte[] a, byte[] b)
  {
  ArrayList retArray = new ArrayList();
  for (int i = 0; i < a.Length; i++)
  {
  retArray.Add(a[i]);
  }
  for (int i = 0; i < b.Length; i++)
  {
  retArray.Add(b[i]);
  }
  return (byte[])retArray.ToArray(typeof(byte));
  }
  }
}

这样方便别人看 --------------------编程问答-------------------- 我还以为有结果了 结果是没结果,虚惊一场!!! --------------------编程问答-------------------- --------------------编程问答-------------------- 求助没求到  多学了一招论坛可用C#CODE 哈哈//[/code=C#][//code] --------------------编程问答-------------------- 等你等到心慌慌 就是没人理 --------------------编程问答-------------------- 难道没人会??? --------------------编程问答-------------------- mark
难道是你Install那边地址计算有误?不懂
http://9200cs.blog.163.com/blog/static/134577369201041111656659/

--------------------编程问答-------------------- 可能是网站编码的问题吧,由于不同网站编码不一样,所以在我们收到字节后应该解码,如大陆的网站一般都是用GB2312的编码方式,而英文为主的国家大多用的UTF-8编码,所以先确定网站编码,然后再根据网站的编码解码。 --------------------编程问答-------------------- --------------------编程问答-------------------- 你确定RECV的时候有数据过来? --------------------编程问答--------------------
引用 11 楼 xu_2007 的回复:
你确定RECV的时候有数据过来?

确定有,且没有丢包,我用别的网络分析软件测试过 ,但打开的网页就是乱码,按理说不是编码的问题,因我拦截的都是BYTE[],我都没做修改与丢弃
且我看了网上很多别的语言写的HOOK  SEND 与 RECV  两个函数的结构是一样的,HOOK方法也一样
但HOOK SEND就没任何错误

HOOK RECV就打开网页全是乱码  以打开百度首页为例(HOOK RECV后显示网页内容为):
TP/1.1 304 Not Modified Date: Mon, 28 Mar 2011 14:21:01 GMT Server: Apache Connection: Keep-Alive ETag: "fe-489c57321cc00" Expires: Thu, 25 Mar 2021 14:21:01 GMT Cache-Control: max-age=315360000  --------------------编程问答-------------------- 虽在网上找到了可能是的答案,请高手帮实现一下代码,一定追加我的全部分

就是我收到的封包可能还是不全的包,都是我的错

答:时机的问题,有些HOOK必须在API的开头,有些必须在API的尾部,比如HOOK CreateFilaA(),
如果你在API尾部HOOK API,那么此时你就不能写文件,甚至不能访问文件;HOOK RECV(),
如果你在API头HOOK,此时还没有收到数据,你就去查看RECV()的接收缓冲区,里面当然没有
你想要的数据,必须等RECV()正常执行后,在RECV()的尾部HOOK,此时去查看RECV()的缓冲区,
里面才有想要的数据;
--------------------编程问答-------------------- 可是从逻辑上讲又不对  如果程序是通过API RECV函数进行接收数据的,那在RECV运行时必须带上参数,不可能在函数运行中传递数据包进来,所以更是不懂错在哪里 --------------------编程问答-------------------- 这个很简单,基于IO的通讯都是不会一次收完数据的,每次的RECV都需要接收需要的包为止
int ret = recv(s, buffer, len, flag);
ret就是已经接收的包,len是包长,因此你要多次进行recv才可以收到完整的包,再拼包处理,例如
int ret = recv(s, buffer, len, flag);
while(ret!=len)
{
    int nRec=recv(s, buffer, len, flag);
    if(nRec==0)
        break;//网络封包不完整
    ret +=nRec;
}
recv只是从缓存中获取已经接收完成的数据,对于数据包是否完整不做处理,因此前端要进行拼包,但是如果
recv收到0个字节,则网络中断 --------------------编程问答-------------------- 楼上大哥  你这简直就是乱写代码呀,recv是已经调用了API发送数据包  返回INT值而已  再说我试了 没有封包内是空的 --------------------编程问答-------------------- 1  byte[] buffer = new byte[len];
2  Marshal.Copy(buf, buffer, 0, len); //读封包数据  
3  send_Hook.Suspend(); //暂停拦截,转交系统调用   
4  int ret = recv(s, buffer, len, flag); //发送数据,此处可对包进行处理操作
5  send_Hook.Continue(); //恢复HOOK   

我都无语了 recv还没调,你读个啥数据.
1,2行放到5行后面去 --------------------编程问答-------------------- 小伙子,不要看我简写的代码,要看懂整个逻辑顺序和过程,找准自己的错误点,我们给你一个错误原理,代码需要你自己去整理,你要读懂我的代码原因就明白你的代码错在哪了 --------------------编程问答--------------------
引用 17 楼 pankun 的回复:
1 byte[] buffer = new byte[len];
2 Marshal.Copy(buf, buffer, 0, len); //读封包数据  
3 send_Hook.Suspend(); //暂停拦截,转交系统调用  
4 int ret = recv(s, buffer, len, flag); //发送数据,此处可对包进行处理操作
5 send_Hook.Contin……

看来哥也没明白我的代码,
1 申明一个数组,
2 从缓存中读取此包的数据
3 暂停,把HOOK的RECV函数还原到不HOOK的状态
4 把封包调用真正在RECV函数去处理
5 把RECV函数HOOK掉(改掉RECV函数的入口为自己的函数),单线程足够用了,且HOOK 发送的包SEND完正确,

按Carpathia 在15楼所提示的,虽给了我提示,但没有用,因代码表达不出来
1 byte[] buffer = new byte[len];
2 Marshal.Copy(buf, buffer, 0, len); //如果这是从缓存中复制数据包,变量buffer又如何叛断封包有没有接收完成,从buffer中跟本看不出来,因byte[]要申明因定长度????求高人解决一下????? --------------------编程问答-------------------- 但我用最笨的办法试了 就等我用等待一秒后再读取数据也出错,13楼提出的理论不合理  不可能1秒后数据没构造到缓存,或者要真正HOOK RECV函数的尾部才行.也许是RECV函数的内部功能问题
唉  我真是没办法了,期待高手出现
System.Threading.Thread.Sleep(1000);
Marshal.Copy(buf, buffer, 0, len); 
打开网页照样是乱码呀 --------------------编程问答-------------------- 自己从另一种思路实现了 ,但接收到的TCP包大小都是65536 ,简直与易语言的网截一模一样,如果要处理这样大的网络包,估计开多线程都没法分析!!!!
 实现过程  
申明API函数:
        [DllImport("ws2_32.dll")]
        static extern int recv(int s, IntPtr buf, int len, int flag);
虽SEND函数参数可用byte[] buf,但我改了过了 recv我用IntPtr buf

在调用时我从缓存中用:Marshal.Copy(buf, buffer, 0, len); //读封包数据
//中间是处理封包....此处省略1000字!呵呵
我处理完后再用Marshal.Copy把封包复制到缓存中
调用真正的recv函数处理,成功了, 但问题来了  TCP包大小都是65536  气死人,易语言中有个网截组件支持库也有此BUG!!!
///////////////////////
-buffer {byte[65536]} byte[]//这是调试过程中的局部变量,复制过来的,分析工具中实际包只有byte[231],但怪的事被HOOK的程序完全正常 --------------------编程问答-------------------- 前辈给你指点,你还瞎质疑,pankun给你当明灯,你当人家来照耗子的。


麻烦你把recv搞明白再来写socket,跟send能一样HOOK吗?
int toProc(int s, IntPtr buf, int len, int flag)
  {
  send_Hook.Suspend(); //暂停拦截,转交系统调用   
  int ret = recv(s, buffer, len, flag); //发送数据,此处可对包进行处理操作

  if (ret == -1) //SOCKET_ERROR
  return ret;

  send_Hook.Continue(); //恢复HOOK   

  byte[] buffer = new byte[len];
  Marshal.Copy(buf, buffer, 0, len); //读封包数据  

  return ret;
  }


m_SockHook.Suspend(); //暂停拦截,转交系统调用
int ret = recv(s, buf, len, flag); //接收数据
if (ret == -1) //SOCKET_ERROR
return ret;
byte[] buffer = new byte[len];
Marshal.Copy(buf, buffer, 0, len); //读封包数据

m_SockHook.Suspend(s, buffer);
m_SockHook.Continue(); //恢复HOOK
return ret; --------------------编程问答-------------------- int ret = recv(s, buffer, len, flag); //发送数据,此处可对包进行处理操作



这行错了,要改成

int ret = recv(s, buf, len, flag); 

recv的DLL引用也要改下,recv是执行完以后才有数据到buffer,传个临时变量进去是没用的 --------------------编程问答-------------------- e,,还有一处错误

byte[] buffer = new byte[len];
Marshal.Copy(buf, buffer, 0, len); //读封包数据


要改成

byte[] buffer = new byte[ret];
Marshal.Copy(buf, buffer, 0, ret); //读封包数据



实际上不会一次性接收完 --------------------编程问答-------------------- 如果把hook中的charset改为auto就不会出现乱码了,但sendProc和toProc却进入不了了。
  #region Api声明
        [DllImport("Kernel32.dll", EntryPoint = "GetModuleHandleA", CharSet = CharSet.Auto)]
        static extern IntPtr GetModuleHandle(
        string lpModuleName
        );
        [DllImport("Kernel32.dll")]
        static extern bool VirtualProtect(
        IntPtr lpAddress,
        int dwSize,
        int flNewProtect,
        ref int lpflOldProtect
        );
        [DllImport("Kernel32.dll", EntryPoint = "lstrcpynA", CharSet = CharSet.Auto)]
        static extern IntPtr lstrcpyn(
        byte[] lpString1,
        byte[] lpString2,
        int iMaxLength
        );
--------------------编程问答-------------------- 楼主的代码看了。不过我试了,收不到接收的数据。
不过乱码这问题。不是这么简单。因为你要确定你收到的都是文字。有些传给你的不一定是文件也有可能是图片。
--------------------编程问答-------------------- 再顶一顶,我什么进不到 sendProc 内? --------------------编程问答-------------------- 因为 recv 和 send  第二个参数的类型 是不一样的。

recv 第二个参数是传址的。
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,