今天没事的时候学了一下easyhook来hook本进程API,确实很简单就能hook。然后想到这个问题:替换webbrowser请求的文件为本地文件。有什么用就不说了,都懂。因为没有用API写过http方面的东西,所以先hook了几个函数,其中InternetReadFile是webbrowser用来获取文件的,而文件句柄可以来源于internetopenurl和 HttpOpenRequest等API,挨个下一下钩子就知道用的是 HttpOpenRequest。当然,获取方法是多种多样的,也可以用x64dbg等调试工具。确定下来hook这两个可以达成目标就可以了:

1、从HttpOpenRequest知道要下载的是哪个文件,过滤需要替换的那个。

2、HttpOpenRequest的返回值就是打开文件句柄了,在InternetReadFile中识别这个句柄就可以。

3、在给InternetReadFile的自定义例程中,对需要替换的文件进行替换。

  接下来,让我们考虑一下替换的流程:webbrowser调用InternetReadFile的时候,先到达我们的自定义例程,然后由我们调用API函数InternetReadFile,于是我们可以在自定义函数中把相应文件句柄的数据请求吃掉——把我们的数据写入缓冲区,而后直接返回不掉用API。对于我们不关心的文件句柄调用API。

  查看InternetReadFile的API声明:

    <DllImport("wininet.dll", SetLastError:=True)>
Public Shared Function InternetReadFile(ByVal hFile As IntPtr, ByVal lpBuffer As IntPtr, ByVal dwNumberOfBytesToRead As Integer, ByRef lpdwNumberOfBytesRead As Integer) As Boolean
End Function

可以知道,第一个参数是HttpOpenRequest返回的文件句柄,lpbuffer是接收数据的缓冲区,dwnumberofbytestoread是期望读取的字节数,即缓冲区大小,lpdwnumberofbytesread是实际写入缓冲区的字节数。返回值表示函数调用是否成功。所以,当lpdwnumberofbytesread为0且函数返回值为true时,达到文件结尾——函数调用成功却没有数据写入,说明数据写完了。于是,在我们的自定义例程中也需要遵守该约定——当写入数据时返回实际写入的数据大小,写完数据后再次被调用时返回0并进行清理。首先,来看一下比较简单的一个hook:

Imports System.Runtime.InteropServices
Imports System.Text Public Class HookHttpOpenRequest <DllImport("wininet.dll")>
Public Shared Function HttpOpenRequestW(hConnect As IntPtr, szVerb As IntPtr, szURI As IntPtr, szHttpVersion As IntPtr, szReferer As IntPtr, accetpType As IntPtr, dwflags As Integer, dwcontext As IntPtr) As IntPtr
End Function Private Delegate Function HttpOpenRequestDelegate(hConnect As IntPtr, szVerb As IntPtr, szURI As IntPtr, szHttpVersion As IntPtr, szReferer As IntPtr, accetpType As IntPtr, dwflags As Integer, dwcontext As IntPtr) As IntPtr
Private Shared hook As EasyHook.LocalHook = Nothing Friend Shared Sub Install()
Using hook
If EasyHook.NativeAPI.GetModuleHandle("wininet.dll") = IntPtr.Zero Then
EasyHook.NativeAPI.LoadLibrary("wininet.dll")
End If
hook = EasyHook.LocalHook.Create(EasyHook.LocalHook.GetProcAddress("wininet.dll", "HttpOpenRequestW"), New HttpOpenRequestDelegate(AddressOf sendProc), Nothing)
hook.ThreadACL.SetInclusiveACL(New Integer() {})
End Using
End Sub Friend Shared Sub UnInstall()
Using hook
If hook IsNot Nothing Then
hook.ThreadACL.SetExclusiveACL(New Integer() {})
End If
End Using
End Sub Private Shared Function sendProc(hConnect As IntPtr, szVerb As IntPtr, szURI As IntPtr, szHttpVersion As IntPtr, szReferer As IntPtr, accetpType As IntPtr, dwflags As Integer, dwcontext As IntPtr) As IntPtr
Dim uri As String = Marshal.PtrToStringUni(szURI)
Dim result As IntPtr = HttpOpenRequestW(hConnect, szVerb, szURI, szHttpVersion, szReferer, accetpType, dwflags, dwcontext)
If uri.Contains("/56896-20170216102630488-270057596.jpg") Then '根据名称区分要替换的图片.
HookInternetReadFile.CheatFileHandle = result End If Return result End Function End Class

easyhook用起来确实比较简单,首先是注入过程,因为webbrowser对wininte.dll的加载是请求第一个页面时,所以可能导致这个DLL不在进程空间,那么先加载它。之后的hook非常易懂(函数名我没有修改,复制粘贴的之前写的sendhook),唯一需要注意的是实际hook的过程调用的是ThreadACL.SetInclusiveACL,unhook类似。在自定义函数中,首先调用API,得到句柄,然后根据要替换的名称来确定是否启动给InternetReadFile的自定义例程。而后,看一下对数据的处理过程:

Imports System.IO
Imports System.Runtime.InteropServices Public Class HookInternetReadFile
<DllImport("wininet.dll", SetLastError:=True)>
Public Shared Function InternetReadFile(ByVal hFile As IntPtr, ByVal lpBuffer As IntPtr, ByVal dwNumberOfBytesToRead As Integer, ByRef lpdwNumberOfBytesRead As Integer) As Boolean
End Function Private Delegate Function InternetReadFileDelegate(ByVal hFile As IntPtr, ByVal lpBuffer As IntPtr, ByVal dwNumberOfBytesToRead As Integer, ByRef lpdwNumberOfBytesRead As Integer) As Boolean
Private Shared hook As EasyHook.LocalHook = Nothing Friend Shared CheatFileHandle As IntPtr = IntPtr.Zero '要替换的文件的句柄,来源于HttpOpenRequest的返回值。
Friend Shared CheatFile() As Byte = File.ReadAllBytes(My.Application.Info.DirectoryPath & "\abc.jpg") '用于替换的文件
Private Shared curcnt As Integer = Friend Shared Sub Install()
Using hook
If EasyHook.NativeAPI.GetModuleHandle("wininet.dll") = IntPtr.Zero Then
EasyHook.NativeAPI.LoadLibrary("wininet.dll")
End If
hook = EasyHook.LocalHook.Create(EasyHook.LocalHook.GetProcAddress("wininet.dll", "InternetReadFile"), New InternetReadFileDelegate(AddressOf sendProc), Nothing)
hook.ThreadACL.SetInclusiveACL(New Integer() {})
End Using
End Sub Friend Shared Sub UnInstall()
Using hook
If hook IsNot Nothing Then
hook.ThreadACL.SetExclusiveACL(New Integer() {})
End If
End Using
End Sub Private Shared Function sendProc(ByVal hFile As IntPtr, ByVal lpBuffer As IntPtr, ByVal dwNumberOfBytesToRead As Integer, ByRef lpdwNumberOfBytesRead As Integer) As Boolean
If hFile = CheatFileHandle Then
If curcnt = CheatFile.Length Then
CheatFileHandle = IntPtr.Zero
curcnt =
lpdwNumberOfBytesRead =
Else
If curcnt + dwNumberOfBytesToRead <= CheatFile.Length Then
lpdwNumberOfBytesRead = dwNumberOfBytesToRead
Marshal.Copy(CheatFile, curcnt, lpBuffer, lpdwNumberOfBytesRead)
curcnt += dwNumberOfBytesToRead
Else
lpdwNumberOfBytesRead = CheatFile.Length - curcnt
Marshal.Copy(CheatFile, curcnt, lpBuffer, lpdwNumberOfBytesRead)
curcnt = CheatFile.Length
End If
End If
Return True
Else
Return InternetReadFile(hFile, lpBuffer, dwNumberOfBytesToRead, lpdwNumberOfBytesRead)
End If
End Function End Class

因为是一个基本结构范例,所以偷懒直接用了参数,这里应该有错误处理过程才行。hook的过程和前面一致,只是在自定义函数中处理把自定义数据写入缓冲区然后返回了。具体的API过程没有跟踪,所以不知道不调用InternetReadFile这个API会不会有内存泄漏之类的什么问题,如果需要调用也非常简单:在恰当的时机循环读取一次就可以了。最后是窗体代码:

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
wb.Navigate("http://images2015.cnblogs.com/blog/56896/201702/56896-20170216102630488-270057596.jpg")
End Sub Private Sub butGotoUrl_Click(sender As Object, e As EventArgs) Handles butGotoUrl.Click
wb.Refresh()
End Sub Private Sub chkCheat_CheckedChanged(sender As Object, e As EventArgs) Handles chkCheat.CheckedChanged
If chkCheat.Checked Then
HookHttpOpenRequest.Install()
HookInternetReadFile.Install()
Else
HookHttpOpenRequest.UnInstall()
HookInternetReadFile.UnInstall()
End If
End Sub End Class

窗体上一个webbrowser重命名为wb,一个checkbox重命名为chkcheat,一个button重命名为butgotourl,另外,在程序所在目录放一个abc.jpg。对,这个范例就是这么简陋,好在现在就可以测试了。如果图片换名字了,那需要修改url地址的同时修改

If uri.Contains("/56896-20170216102630488-270057596.jpg") Then '根据名称区分要替换的图片

才可以正确运行。

修改http请求文件为本地文件的一种方法:hook InternetReadFile 和 HttpOpenRequest的更多相关文章

  1. chrome浏览器调试线上文件映射本地文件

    chrome浏览器调试线上文件映射本地文件 通过ReRes让chrome拥有路径映射的autoResponse功能. 前端开发过程中,经常会有需要对远程环境调试的需求.比如,修改线上bug,开发环境不 ...

  2. iOS 获取文件的目录路径的几种方法 [转]

    iOS 获取文件的目录路径的几种方法 2 years ago davidzhang iphone沙箱模型的有四个文件夹,分别是什么,永久数据存储一般放在什么位置,得到模拟器的路径的简单方式是什么. d ...

  3. 提交(post)xml文件给指定url的2种方法

    原文:提交(post)xml文件给指定url的2种方法 1  这段代码是在网上搜到的,拿来共享,项目正好要用到.其中的data你只需要传递一个xml字符串就可以 protected   string  ...

  4. 使用c#检测文件正在被那个进程占用 判断文件是否被占用的两种方法

    C# 判断文件是否被占用的三种方法 using System.IO; using System.Runtime.InteropServices; [DllImport("kernel32.d ...

  5. 关于FileZilla上传文件后服务器端文件与本地文件大小不一致的解决方法

    最近在调试网站时发现,通过ftp上传工具FileZilla上传至服务器端的文件与本地文件大小不一致,虽然没有影响网站的最终显示效果,但仍让我困惑不解.后发现是传输类型的原因,解决方法如下: 中文版Fi ...

  6. datagrid数据导出到excel文件给客户端下载的几种方法

    方法一:导出到csv文件,存放在服务器端任一路径,然后给客户下载 优点: 1.可以进行身份认证后给客户下载,如果放到非web目录就没有对应的url,客户无法随时下载. 2.也是因为生成了文件,所以占用 ...

  7. 通过InputStream访问文件中的数据的四种方法

    //方法一(每次只读取一个字节) public static void getFile() throws IOException { File file = new File("D:\\a. ...

  8. [C#.Net]判断文件是否被占用的两种方法

    今天开发产线测试Tool时发现日志文件会几率性的被占用,上网浏览找到最简单的代码(API或者FileStream),在这里抛砖引玉下. 第一种方法:API using System.IO; using ...

  9. QML中文件的加载(三种方法)

    在这里小小总结一下QML文件中如何加载QML文件与JavaScript文件. 1.QML文件中加载JavaScript文件 语法: import <ModuleIdentifier> &l ...

随机推荐

  1. 小练习:vaild number

    1.描述 给定字符串,若该字符串表示的是数字,则输出true,否则输出false 2.分析 题目一看感觉不难,做起来却很麻烦,首先是数字的各种表示要知道,然后就是对这些不同形式的数字进行筛选判断.该题 ...

  2. 【网络编程】inet_addr、inet_ntoa、inet_aton、inet_ntop和inet_pton区分

    先上一张图 1.把ip地址转化为用于网络传输的二进制数值 int inet_aton(const char *cp, struct in_addr *inp); inet_aton() 转换网络主机地 ...

  3. 【后台测试】postman简介

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/6266047.html 在接口测试的过程中,经常拿到一个接口 ...

  4. django中如何将多个app归到一个目录下。

    1.当startapps 生成多个app后,为了便于管理,可新建一个apps目录,把应用全部剪切进apps. 如果是在pycharm中,会提示是否自动更新路径,这里要全部选择取消. QQ群交流:697 ...

  5. c# 加密工具类

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Sec ...

  6. 继承users表,添加新字段成一个新表

    1. Tools > Run manage.py Task 创建app,users startapp users 2.修改users中的models from django.db import ...

  7. Android编程 EditView 中如何设置最多可以输入的字符数量 属性 android:ems 与 android:maxLength 的区别

    最近有一个新的感悟,那就是工作的时候千万不要遇到那种特要人无语的领导,很不幸我现在就遇到了这样的一个领导,说是要给领导认识的一个熟人家的孩子写本科毕业设计预算把我给派过去给本科生写毕业设计,这事情的确 ...

  8. phpcms v9取消验证码

    phpcms/modules/admin/index.php// $code = isset($_POST['code']) && trim($_POST['code']) ? tri ...

  9. slam学习

    学习内容: 数学: 线性代数,概率论, 优化理论,离散数学, 李代数, 凸优化: 算法:   概率机器人, 机器人状态估计, 深度学习,非线性优化: 工程: c/c++ , python, ros, ...

  10. apk系统签名命令

    java -jar signapk.jar platform.x509.pem platform.pk8 D:/ClockSetting.apk D:/ClockSettingSigned.apk 需 ...