恶搞英语大厅的原理(准确来说应该是恶搞IE)
源代码下载地址: http://download.csdn.net/detail/linger2012liu/4033059
由于个人知识有限,若发现不对,敬请指正。
恶搞程序的本意应该不是针对我们学校的英语大厅,而是IE。恰好英语大厅是用的是IE内核,于是我们也可以用它来恶搞大厅了。
恶搞是这样子的:修改某网页的源代码(当然是在我们客户端),然后更新显示。正如作者所描述的:“此程序除了恶搞外,其实就是一个IE内容修改的程序!还有什么用途~~
发挥你的想象力吧!“
那么我们恶搞大厅时各个步骤做了什么呢?下面分析一下。首先,点击左上角的“放大镜”并拖到英语大厅,该步骤是获取大厅的窗口句柄,并根据句柄获得该窗口的html代码。然后我们把一段html代码(可能夹杂着javascript代码)插入到代码中的适当位置。这时,大厅会出现一个按钮,点击按钮后,会出发相应的javascript的运行。插入的代码不同,效果也不同,比如可能是显示正确答案,可能是把正确答案都填上去了。
由于现在上不了大厅了,所以我直接拿网页做测试,当然原理都是一样的。首先写以下html代码,另存为test.html。
<html>
<body>
<p>请点击这里</p>
<!-- <pοnmοusedοwn='alert("hehe")'>请点击这里</p> -->
</body>
</html>
打开改网页,将放大镜放到网页上,抓取html代码,恶搞显示如下:
修改以下代码,点击开始恶搞,如下(主要是把注释的代码复制到前面,把点击事件添加到文本中)
这时,剧本应该是这样演的,点击文本后会弹出提示框。但也许是我IE设置问题,出现如下,
然后就点击那里允许执行阻止的内容,其实就是js代码。再点击一下开始恶搞(因为刚才的操作可能自动重新加载页面了)。到网页中,点击文字,出现提示框了。设想成功,恶搞就是修改IE代码的功能。
那么接下来是怎么写一个修改一个IE的网页源代码的程序。查阅了相关的知识,实现主要是这样:打开网页,找到要修改网页的窗口句柄,根据句柄改掉对应网页的内容。这种方法,因为是用窗口句柄做中介的,因此肯定也适合英语大厅等一类用IE内核的程序。
首先新建一个c#的控制台程序,我这里命名为fuckIE,在引用中添加Microsoft.mshtml这个引用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
usingSystem.Runtime.InteropServices;
using mshtml;
namespace fuckIE
{
class Program
{
//一些api函数的声明
[DllImport("USER32.DLL")]
public static extern IntPtr FindWindow(stringlpClassName, string lpWindowName);
[DllImport("user32.dll")]
privatestatic extern IntPtr FindWindowEx(IntPtrhwndParent, IntPtr hwndChildAfter, string lpszClass, stringlpszWindow);
[DllImport("user32.dll", EntryPoint = "EnumChildWindows")]
public static extern bool EnumChildWindows(IntPtrhwndParent, EnumChildProc EnumFunc, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint RegisterWindowMessage(stringlpString);
[DllImport("oleacc.dll", PreserveSig = false)]
[return:MarshalAs(UnmanagedType.Interface)]
public static extern object ObjectFromLresult(UIntPtrlResult, [MarshalAs(UnmanagedType.LPStruct)]Guid refiid, IntPtrwParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(IntPtrhWnd, uint Msg, UIntPtrwParam, IntPtr lParam, SendMessageTimeoutFlags fuFlags, uint uTimeout, out UIntPtr lpdwResult);
public enum SendMessageTimeoutFlags:
uint
{
SMTO_NORMAL = 0x0000,
SMTO_BLOCK = 0x0001,
SMTO_ABORTIFHUNG = 0x0002,
SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
}
public IntPtr destHandle = IntPtr.Zero;
public delegate bool EnumChildProc(IntPtrhwnd, IntPtr lParam);
boolFindChildClassHwnd(IntPtr hwndParent, IntPtr lParam)
{//该函数用来从一个顶级窗口(我这里是IE的打开的某网页)查找类名为Internet Explorer_Server的子窗口
IntPtrhwnd = FindWindowEx(hwndParent, IntPtr.Zero,"Internet Explorer_Server", null);
if(hwnd != IntPtr.Zero)
{
//pfw->m_hWnd= hwnd; // found: save it
destHandle = hwnd;
returnfalse; // stop enumerating
}
EnumChildWindows(hwndParent,FindChildClassHwnd, lParam); // recurse
//每段枚举子窗口,递归调用
returntrue;
}
// keep looking
[STAThreadAttribute]
static void Main(string[]args)
{
// IntPtr ieh = FindWindow("InternetExplorer_Server",null); //这样会失败,因为这只能适合顶级窗口
IntPtrieh = FindWindow(null, "E:\\past\\恶搞\\fuckIE\\fuckIE\\test.html- linger");
//首先找到顶级窗口,第二个参数为窗口标题,即如下图片的左上角的文字
Console.WriteLine("ie句?柄À¨²:êo"+ieh);
E:\past\恶?搞?\fuckIE\fuckIE\test.html - linger
if(ieh == IntPtr.Zero)
{
Console.WriteLine("can not find the window");
return;
}
Programinstance = new Program();
instance.FindChildClassHwnd(ieh, IntPtr.Zero);
//查找类名为InternetExplorer_Server的子窗口
if(instance.destHandle == IntPtr.Zero)
{
Console.WriteLine("can not find the window");
return;
}
Console.WriteLine("目?标À¨º句?柄À¨²"+instance.destHandle);
//目标句柄找对了,注意用spy++找的 是16进制
UIntPtrlRes = new UIntPtr();
uintapp2_GenerateEvent = RegisterWindowMessage("WM_HTML_GETOBJECT");
SendMessageTimeout(instance.destHandle, app2_GenerateEvent, UIntPtr.Zero, IntPtr.Zero,SendMessageTimeoutFlags.SMTO_NOTIMEOUTIFNOTHUNG,1000, out lRes);
//IHTMLDocument得Ì?不?到Ì?body.innerHTML
IHTMLDocument2obj = (IHTMLDocument2)ObjectFromLresult(lRes,typeof(IHTMLDocument).GUID,IntPtr.Zero);
Console.WriteLine(obj.body.innerHTML);
//obj.body.innerHTML= "<p οnmοuseοver='alert(\"hehe\")'>本À?来¤¡ä什º2么¡ä都?没?有®D的Ì?</p>";
obj.body.innerHTML = " <pοnmοusedοwn='alert(\"hehe\")'>请点击这里</p>";
//修改为自己想要的代码
Console.WriteLine(obj.body.innerHTML);
}
}
}
下面测试效果。
打开test.html网页,此时不要点击其他网页, 也不要最小化IE浏览器,不然是找不到该句柄的。(人家恶搞的程序是用拖拽放大镜的方法找的,我对界面编程不是很了解,所以只好用窗口标题找窗口句柄。)
运行程序,
还是出现这个,
点击那里,再点击“允许阻止的内容”,出现下面对话框,再点确定。
关闭刚才打开的控制台程序,再次运行。
到网页中点击那里的文字,实验成功!!!
有个疑问就会如果查看该网页源代码,发现源代码并没有改掉,对浏览器的机理还不是很了解啊。不过猜测,IE这个查看源文件的功能,可能是从服务器那里拿来的,当然我这里是本地文件,服务器就是自己电脑。
一个恶搞程序大概如此。如果把上面的c#代码稍微修改一下, 就可以用到英语大厅了。主要是窗口标题名,和怎么修改html代码。前者比较容易,用任务管理器就可以准确看到进程的窗口标题名,英语大厅也不例外,当然我电脑没有大厅。
至于修改html代码就是做一些字符串处理,还要懂一些javascript。当然,修改html之前,得研究一下源代码,看一下怎么可以用javascript把答案显示出来,或者把答案填上去。
看了之前的两份插入代码,都是在原有html里添加一个button,然后触发一个javascript事件,一是显示正确答案,二是把正确答案填上去。
还有,我们可以恶搞英语大厅的一个前提,是答案已存在我们客户端电脑里,也许做大厅软件的队伍为了方便还是为了减轻服务端的压力,把答案检测放在客户端进行,服务端负责接收分数就可以了。所以,我觉得恶搞大厅有3种渠道,一是分数的来源,二是分数的缓存,三是数据包。第一,我们IE恶搞的方法就是属于对分数的来源做处理,把正确答案填上去,分数自然高。第二,我想在客户端里一定有几个变量用来存储分数,只要修改了他们即可,也许在javascript就有。第三,分数检测出后,肯定会给服务端发送包含分数的数据包,只要伪造一个数据包,发送过去,你想拿多少分都可以。
更正:后来苏淦告诉我上面所说的3种方法后两种还是不行的,因为大厅记录了我们的答案,应该是把我们填的答案也提交上去了吧,不可能傻逼到存在本地磁盘