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的时候有数据过来? --------------------编程问答--------------------
确定有,且没有丢包,我用别的网络分析软件测试过 ,但打开的网页就是乱码,按理说不是编码的问题,因我拦截的都是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行后面去 --------------------编程问答-------------------- 小伙子,不要看我简写的代码,要看懂整个逻辑顺序和过程,找准自己的错误点,我们给你一个错误原理,代码需要你自己去整理,你要读懂我的代码原因就明白你的代码错在哪了 --------------------编程问答--------------------
看来哥也没明白我的代码,
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#