修改http请求文件为本地文件的一种方法:hook InternetReadFile 和 HttpOpenRequest
今天没事的时候学了一下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的更多相关文章
- chrome浏览器调试线上文件映射本地文件
chrome浏览器调试线上文件映射本地文件 通过ReRes让chrome拥有路径映射的autoResponse功能. 前端开发过程中,经常会有需要对远程环境调试的需求.比如,修改线上bug,开发环境不 ...
- iOS 获取文件的目录路径的几种方法 [转]
iOS 获取文件的目录路径的几种方法 2 years ago davidzhang iphone沙箱模型的有四个文件夹,分别是什么,永久数据存储一般放在什么位置,得到模拟器的路径的简单方式是什么. d ...
- 提交(post)xml文件给指定url的2种方法
原文:提交(post)xml文件给指定url的2种方法 1 这段代码是在网上搜到的,拿来共享,项目正好要用到.其中的data你只需要传递一个xml字符串就可以 protected string ...
- 使用c#检测文件正在被那个进程占用 判断文件是否被占用的两种方法
C# 判断文件是否被占用的三种方法 using System.IO; using System.Runtime.InteropServices; [DllImport("kernel32.d ...
- 关于FileZilla上传文件后服务器端文件与本地文件大小不一致的解决方法
最近在调试网站时发现,通过ftp上传工具FileZilla上传至服务器端的文件与本地文件大小不一致,虽然没有影响网站的最终显示效果,但仍让我困惑不解.后发现是传输类型的原因,解决方法如下: 中文版Fi ...
- datagrid数据导出到excel文件给客户端下载的几种方法
方法一:导出到csv文件,存放在服务器端任一路径,然后给客户下载 优点: 1.可以进行身份认证后给客户下载,如果放到非web目录就没有对应的url,客户无法随时下载. 2.也是因为生成了文件,所以占用 ...
- 通过InputStream访问文件中的数据的四种方法
//方法一(每次只读取一个字节) public static void getFile() throws IOException { File file = new File("D:\\a. ...
- [C#.Net]判断文件是否被占用的两种方法
今天开发产线测试Tool时发现日志文件会几率性的被占用,上网浏览找到最简单的代码(API或者FileStream),在这里抛砖引玉下. 第一种方法:API using System.IO; using ...
- QML中文件的加载(三种方法)
在这里小小总结一下QML文件中如何加载QML文件与JavaScript文件. 1.QML文件中加载JavaScript文件 语法: import <ModuleIdentifier> &l ...
随机推荐
- 练习-99乘法表 token生成器 翻译小工具
一.99乘法表 1.1 技术点 记住: for 循环的使用,以及for的嵌套使用 range()的使用,掌握sep为负数的使用的使用. print() 函数的使用,默认的结尾的换行符 替换 end= ...
- html/css/javascript练习代码
这两天心血来潮学习了前端,自己也做了个小小的网页,不好看QAQ 不过网页上集结了很多零碎的知识,在这里先马克一下.图片地址:https://github.com/lesroad/html-css-js ...
- poj1698
题解: 网络流 然后似乎反着做块? 代码: #include<cstdio> #include<cstring> #include<algorithm> #incl ...
- 前端url传递编码问题
JAVASCRIPT中URL 传递参数(特殊字符)解决方法及转码解码的介绍 有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用他们的编码了.下表中列出了一些URL特殊符 ...
- 【Html 学习笔记】第四节——框架
我们经常使用的网页可能不是一个单独的网页,而是多个嵌套的,那么就需要用到下面的技术. 垂直框架:<frameset cols=""> 这里需要注意的是:四个网页需要同时 ...
- 【css】响应式布局入门【转】
最近研究响应式设计框架的时候,发现网上很多相关的属性介绍,却很少有系统的入门级使用的文章,我自己整理了一篇入门知识,并没有什么高深的理论,也不牵扯到框架. 目前已经越来越多的站点以及wap站点使用响应 ...
- 在Vim中使用gtags
之前一直使用vim+ctags+cscope来弄c的代码,最近看同事使用gtags,觉得在搜索方面要高级很多,网上大多都是emacs+gtags的资料,而vim的则比较少,这里搞通了之后,做个记录. ...
- C语言 运算符详细介绍及示例代码
C 运算符 运算符是一种告诉编译器执行特定的数学或逻辑操作的符号.C 语言内置了丰富的运算符,并提供了以下类型的运算符: 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 杂项运算符 本章将逐 ...
- vueRouter中scrollBehavior实现滚动固定位置
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样. vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动. 注意: 这个功能只 ...
- Vue(2) : Vue for Gank.io
简介 最近学习Vue2.0,由于不懂前端知识,学习过程比较缓慢.文档学习过程如下: 通读vue官文 通读vue-router 2中文指南 学习axios 通读vuex官文 数据接口 再次感谢代码家的G ...