另一种修改在线SWF的方法
这次公开的这种方式有一定的局限性,通过努力可以克服。它也有一定的优越性。与以前所用的内存搜索和局部修改不同,这种方式不仅可以局部修改,也可以完全重新编译而不受文件大小的限制。推而广之,这种方法可以替换很多东西而不仅限于SWF,凡是从服务器申请来的都可以。当然,缓存是一个问题,前面也说过,通过努力都可以克服。这种方式基于对网络请求和文件下载API的HOOK,在下面的范例中HOOK本进程的WEBBROWSER控件的调用的两个WININET.DLL的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 以及 <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 其实,HOOK?internetreadfile 就可以达成目标。但是有一个问题,从hfile难于识别正在下载的文件是哪一个,而从lpbuffer来识别需要大量对比。所以,我们来查看一下hfile的来源,它可以由好几个API获得,而httpopenrequest正是webbrowser所使用的那个。通过HOOK 就可以得到szuri,它就是请求的文件,而函数返回值就是hfile这个家伙。所以,HOOK这两个API看起来是不错的做法。 然后,让我们分析一下具体实现过程:我们知道,HOOK internetreadfile之后,这个API被调用时会进入我们的自定义函数,而在自定义函数中会调用真正的API来填充其传入的lpbuffer所指向的数组。那么机会来了,如果我们识别到一个hfile,它恰好是我们想要替换掉的那个请求来的文件,这时我们不调用真正的API而自己填充lbuffer就把请求的文件换成了本地文件。而这时服务器和webbrowser还蒙在鼓里。当然,真正实施之前,我们还需要的理解一下internetreadfile的调用方法,否则一切都是空谈。可以在网上找一些使用这个API的代码来查看一下,很容易得到这样的结论:这个API被循环调用,直到lpdwnumberofbytesread为0且返回值为true。此时,将历次读取的缓冲区内容放在一起,就是整个文件。 于是,我们替换时,无需担心调用多少次,webbrowser会不断调用,每调用一次我们就写入一次数据,并模仿API的其他行为:设置真实写入字节数,如此直到数据被完全写入。此时,该API还会被调用,因为我们从来都设置lpdwnumberofbytesread为写入lpbuffer的字节数,那么现在没有数据写了,所以实际写入数据数为0,亦即lpdwnumberofbytesread为0时,调用者认为文件传输完成。这与我们用这个API去编写程序的思路是完全一样的。 因为今天看了看easyhook,所以当作练习就直接用了这个第三方库。如果想自己完成hook部分,可以参考我cnblogs的博客。在x64上,这两个API中至少有一个前面没有90的,所以自己实现的话,简单起见还是使用EATIAT HOOK。而直接使用easyhook或者微软库都没有这些问题,虽然它们也都采用inlinehook。所以,下面的代码都是基于easyhook的,使用前先下载这个库并放置easyhook.dll,easyhook32.dll,easyhook64.dll到程序目录。 1、hook httpopenrequestw Imports System.Runtime.InteropServices Public Class HookHttpOpenRequest <DllImport("wininet.dll")> Public Shared Function HttpOpenRequestW(hConnect As IntPtr,dwcontext As IntPtr) As IntPtr End Function Private Delegate Function HttpOpenRequestDelegate(hConnect As IntPtr,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() {0}) End Using End Sub Friend Shared Sub UnInstall() Using hook If hook IsNot Nothing Then hook.ThreadACL.SetExclusiveACL(New Integer() {0}) End If End Using End Sub Private Shared Function sendProc(hConnect As IntPtr,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("bd_logo1_31bdc765.png") Then '根据名称区分要替换的图片. HookInternetReadFile.CheatFileHandle = result End If Return result End Function End Class 这个非常简单,没有什么好说的。哦对了,那个替换函数的名就不用关心了,代码是在工程中另外一个类sendhook.vb里面复制的,忘了改名。然后是另一个: 2、hook internetreadfile Imports System.IO Imports System.Runtime.InteropServices Public Class HookInternetReadFile <DllImport("wininet.dll",ByRef lpdwNumberOfBytesRead As Integer) As Boolean End Function Private Delegate Function InternetReadFileDelegate(ByVal hFile As IntPtr,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 = 0 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() {0}) End Using End Sub Friend Shared Sub UnInstall() Using hook If hook IsNot Nothing Then hook.ThreadACL.SetExclusiveACL(New Integer() {0}) End If End Using End Sub Private Shared Function sendProc(ByVal hFile As IntPtr,ByRef lpdwNumberOfBytesRead As Integer) As Boolean If hFile = CheatFileHandle Then If curcnt = CheatFile.Length Then CheatFileHandle = IntPtr.Zero curcnt = 0 lpdwNumberOfBytesRead = 0 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,lpdwNumberOfBytesRead) curcnt = CheatFile.Length End If End If Return True Else Return InternetReadFile(hFile,dwNumberOfBytesToRead,lpdwNumberOfBytesRead) End If End Function End Class 作为一个demo,还需要一个窗体: 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 butRef_Click(sender As Object,e As EventArgs) Handles butRef.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
各个名称都可以顾名思义,具体就不说了。怎么样,这个还算是一个比较好玩的玩具吧。
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |