| 关键词: 钩子 线程 整数型 函数 消息 进程 子程序 API 果真 一个 |
来源:http://www.heilang.co.cc/blog/article/hack/30.htm 看到这个标题,对英语不太有好感的你一定很高兴吧。终于可以编写真正属于自己的程序了,这可是我等小菜梦寐以求的事啊!(梦想中,仿佛自己成了又一个“葛军”。什么你问我葛军是谁?你不会连大名鼎鼎的灰鸽子是谁写的都不知道吧?) 易语言是一款功能强大,类似于VB的跨平台全中文内核的编程语言。我们今天就用它制作一个注入到远程线程的程序。 启动易语言4.02版(目前最新版),我们可以看到界面,选择第一项,新键一个“Windows 窗口程序”。 然后会出现一个启动窗口,双击它来到代码编辑界面。从这里可以看到易语言把各个程序段以表格的形式表现出来了,非常直观。 易语言是用支持库来扩展功能的,程序初始时只加载了系统核心支持库,如果你还需要其他支持库的功能就要手工添加,方法是点击“工具”→“支持库配制”,然后添加需要的支持库。其中本文的例程就用到了应用接口支持库。 基础知识 知识点1:API 什么是API?API全称为Application Programming Intererface,直译的话可叫它“应用程序接口”。API说到底就是一系列的函数,我们可以把它理解为程序的子程序或子过程,只不过它是系统本身提供给用户在进行高级编程中供其程序使用。我们通过在易语言应用程序中声明外部过程,就能够访问使用Windows API。 知识点2: 钩子 钩子(Hook),是Windows消息处理机制的一个平台。应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理Windwos消息或特定事件。 钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递 使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中。SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。线程勾子用于监视指定线程的事件消息。线程勾子一般在当前线程或者当前线程派生的线程内,系统勾子监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含“钩子回调函数”的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入到目的进程。 知识点3:线程注入方式 线程注入通常有以下几种注入方式: 1.利用注册表: 该方法仅适用于Windows NT/2000操作系统,且为了激活或停止钩子的注入,必须重新启动Windows,非常不方便。 2. 建立系统范围的Windows钩子向某个进程注入DLL。一个十分普遍也是比较简单的方法,这也是本文所使用的方法,就是建立在标准的Windows钩子的基础上。我们知道,不同进程中使用的DLL之间是不能直接共享数据的,因为它们活动在不同的地址空间中。但在Windows钩子DLL中有一些数据,如Windows钩子句柄HHook,这是由SetWindowsHookEx函数返回值得到的。并且作为参数将在CallNextHookEx函数和UnhookWindoesHookEx函数中使用,显然使用SetWindowsHookEx函数的进程和使用CallNextHookEx函数的进程一般不会是同一个进程,因此我们必须能够使句柄在所有的地址空间中都是有效且有意义的。也就是说,它的值必须在这些钩子DLL所挂钩的进程之间是共享的。我们可以利用内存映像技术来申请使用一块各进程可以共享的内存区域,主要是利用了CreateFileMapping和MapViewOfFile及OpenFileMapping这3个API函数。这是一个通用的方法,适合所有的开发语言,只要它能使用Windows的API,那么这种机制在Win 9x/Me和Win NT/2K中都是可以得到支持的。 3. 使用 CreateRemoteThread函数也不错,不过它只支持在Windows NT/2000下使用。 编程思路 通过建立系统范围的Windows钩子,把我们要插入执行的代码所在的DLL注入到被插进程。程序分两个部分:一个启动程序,这里取名Start.exe,主要负责安装/释放钩子和写入进程共享数据。另一个是个动态连接库(DLL),这里取名HookDLL.dll,主要负责读入共享数据和在目标建立新的线程。 Start.exe通过安装Windows全局消息钩子WH_GETMESSAGE,把HookDLL.dll注入到其他进程里,Start.exe随即进入消息循环。在钩子回调函数中,判断当前进程ID是先前Start.exe 查找到的Explorer进程ID。如果是则创建一个新线程,该新线程首先往Start.exe消息队列放置一个线程退出消息WM_QUIT导致其消息循环结束,此时插入线程完成。 代码实现 接下来是具体的实现代码了,我会逐行分析,希望能照顾到新手。 先介绍几个关键API; SetWindowsHookExA,把一个应用程序定义的钩子子程安装到钩子链表中。 CallNextHookEx,执行钩子链表所指的下一个钩子子程。 UnHookWindowsHookEx, SetWindowsHookExA的反操作(卸载钩子)。 CreateFileMapping,创建一个新的文件映射对象 MapViewOfFile,将一个文件映射对象映射到当前应用程序的地址空间。 UnmapViewOfFile,指定要解除映射的一个文件映射的基准地址,这个地址是早先用MapViewOfFile函数获得的。 OpenFileMappingA,打开一个现成的文件映射对象。 PostThreadMessage,将一条消息投递给应用程序的线程。 GetMessage,从调用线程的消息队列中取出消息并将其放入MSG结构。 了解了这几个API,离成功也就不远了。先来看看我写的钩子安装/释放的子程序(函数),同时你也会惊喜地发现原来易语言的代码可读性这么强,整个结构清晰明朗,一目了然。 注:在易程序里面,符号“ ' “后面代表的是注释 下面是详细代码: .子程序 GetMsgHookOn, 整数型, 公开, 安装全局消息钩子 .参数 DLLPath, 文本型 .局部变量 hMod, 整数型 .局部变量 lpProc, 子程序指针 hMod = api_LoadLibraryA (DLLPath) lpProc = api_GetProcAddress (hMod, “GetMsgProc”) hook = api_SetWindowsHookExA (#WH_GETMESSAGE, lpProc, hMod, 0) 返回 (hook) .子程序 GetMsgHookOff, 逻辑型, 公开, 关闭全局消息钩子 返回 (api_UnhookWindowsHookEx (hook)) 这两个子程序封装在HookDLL.dll里,Start.exe通过外部DLL声明,就可以在程序里调用,下面我们来解析Start.exe的核心代码: Start.exe通过API函数CreateFileMapping创建一个内存映射文件“HookExplorer8Mazi ”,并通过API函数MapViewOfFile把它映射到本进程空间,然后安装在HookDLL里的WH_GETMESSAGE 全局钩子,再结合易语言自带的“写到内存()”命令,把下列4个数据写到映射文件,以便其他进程(explorer.exe)可以调用: 1.通过子程序取进程PID取得的explorer.exe的进程ID 2.API函数GetCurrentThreadId ()取得的自身线程ID 3. HookDLL的全路径名 4.安装全局钩子所返回的钩子句柄,以便在钩子回调函数中的api_CallNextHookEx使用。 接下来程序Start.exe便进入了消息循环,当收到新线程发来的退出消息后,程序就开始脱离WH_GETMESSAGE钩子,完成关闭映射文件等善后工作,最后退出程序。 下面是Start.exe的核心代码: ' 收集要写入的数据 DLLPath = 取运行目录 () + “\HookDLL.dll” Explorer_PID = 取进程PID (“explorer.exe”) ' 取进程PID,这个子程序请查看源码,利用了易的应用接口支持库 当前线程标志符= api_GetCurrentThreadId () .如果真 (Explorer_PID = 0 或 当前线程标志符= 0) 信息框 (“寻找指定进程出错!”, 0, ) 结束 () .如果真结束 Main = 到字节集 (Explorer_PID) + 到字节集 (当前线程标志符) + 到字节集 (DLLPath) + { 0, 0, 0 } ' 给要写入的3个数据赋值,字符串是以两字节的0为结束标志的,所以这里加上{ 0, 0, 0 } FileMapH = api_CreateFileMapping (-1, nil, #PAGE_READWRITE, 0, 取字节集长度 (Main) + 4, “HookExplorer8Mazi”) ' 创建内存映射文件 TheNodeP = api_MapViewOfFile (FileMapH, #FILE_MAP_ALL_ACCESS, 0, 0, 0) ' 映射到本进程空间 hhook = GetMsgHookOn (DLLPath) ' 挂DLL跳板钩子 .如果真 (hhook = 0) 输出调试文本 (“挂DLL跳板钩子失败!”) Api_CloseHandle (FileMapH) 结束 () .如果真结束 写到内存 (到字节集 (hhook) + Main, TheNodeP, ) ' 写入前面提到的4个数据 Api_UnmapViewOfFile (TheNodeP) ' 关闭内存映射 .循环判断首 () ' 循环接收线程消息. .循环判断尾 (api_GetMessage (ThreadMessage, 0, 0, 0)) ' 收到插入线程发来的消息,开始脱钩子… Api_GetMsgHookOff () ' 脱DLL跳板钩子 Api_CloseHandle (FileMapH) ' 关闭文件映射对象 结束 () 在我们DLL里的钩子回调函数中,首先读取start.exe在内存中创建的映射文件的3个数据,然后根据这些数据判断当前进程ID是否是先前Start.exe 查找到的Explorer进程ID,。是的话,则再次LoadLibrary(HookDLL.dll),并定位到其中ThreadPro函数。此时创建一个新线程,线程函数就是ThreadPro。 钩子回调函数关键代码如下: .子程序 GetMsgProc, 整数型, 公开, 钩子回调函数 .参数 code, 整数型 .参数 wParam, 整数型 .参数 lParam, 整数型 .局部变量lpThreadA, SECURITY_ATTRIBUTES .局部变量 ThreadPt, 整数型 .局部变量 ThreadID, 整数型 .局部变量 LibraryH, 整数型 读取共享数据 () ' 这个子程序,在下面有介绍 .如果真 (TheNodeP ≠ 0 且 DLL.ExplorerID ≠ 0 且 GetCurrentProcessId () = DLL.ExplorerID) ' 是资源管理器 .如果真 (倒找文本 (DLL.MainPath, “.dll”, , 真) ≠ -1) ' DLL路径是否正确 LibraryH = api_LoadLibraryA (DLL.MainPath) ' 装载动态链接库 .如果真结束 .如果真 (LibraryH ≠ 0) ThreadPt = 到整数 (api_GetProcAddress (LibraryH, “ThreadPro”)) ' 定位线程函数 .如果真 (ThreadPt ≠ 0) Api_CreateThread (lpThreadA, 0, ThreadPt, 0, 0, ThreadID) ' 创建新线程 .如果真结束 .如果真结束 .如果真结束 返回 (api_CallNextHookEx (hhook, code, wParam, lParam)) ' 钩子循环 下面是子程序读取共享数据的代码: .子程序 读取共享数据 .局部变量 i, 整数型 .局部变量 MainPath, 文本型 .局部变量 FileMapH, 整数型 FileMapH = api_OpenFileMapping (#FILE_MAP_ALL_ACCESS, 0, “HookExplorer8Mazi”) .如果真 (FileMapH ≠ 0) TheNodeP = api_MapViewOfFile (FileMapH, #FILE_MAP_ALL_ACCESS, 0, 0, 0) .如果真 (TheNodeP ≠ 0) ' 取回共享数据(每个整数型数据占4个字节的内存空间) hhook = 取字节集数据 (指针到字节集 (TheNodeP, 4), 3, ) DLL.ExplorerID = 取字节集数据 (指针到字节集 (TheNodeP + 4, 4), 3, ) DLL.MainThread = 取字节集数据 (指针到字节集 (TheNodeP + 8, 4), 3, ) DLL.MainPath = 指针到文本 (TheNodeP + 12) .如果真结束 API_UnmapViewOfFile (TheNodeP) ' 关闭内存映射 api_CloseHandle (FileMapH) ' 关闭文件映射对象 .如果真结束 该新线程首先往Start.exe消息队列放置一个线程退出消息WM_QUIT,导致其消息循环结束。此时插入线程完成,可以看到屏幕左上角不断变化的数字,这说明我们的代码正在执行。进程列表却没有Start.exe,用进程管理观察,可发现Explorer进程。的确多了个线程,且来自HookDLL.dll。如果希望插入Explorer的线程结束,按Alt+L 即可。 关键代码如下: 子程序 ThreadPro, , 公开, 待插线程代码 .局部变量 Count, 整数型 .局部变量 theMsg, MSG .局部变量 FileMap, 整数型 .局部变量 nil, SECURITY_ATTRIBUTES .局部变量 HotKeyID, 整数型 ' 关键的一步,向start.exe发送线程退出消息 ,通知start.exe线程插入完成。 Api_PostThreadMessage (DLL.MainThread, #WM_QUIT, 0, 0) ' 下面是自定义代码 FileMap = api_OpenFileMapping (#FILE_MAP_ALL_ACCESS, 0, “hacker0058Explorer8Mazi”) ' 禁止重复运行 CloseHandle (FileMap) .如果真 (FileMap = 0) FileMap = api_CreateFileMapping (-1, nil, 4, 0, 2, “hacker0058Explorer8Mazi”) Count = 0 ‘申请全局原子 HotKeyID =api_GlobalAddAtomA (“hacker0058”) ‘注册热键ALT+L Api_RegisterHotKey (0, HotKeyID, 1, #L键) .判断循环首 (api_PeekMessage (theMsg, 0, 0, 0, #PM_REMOVE) = 0 且 取反 (theMsg.message = #WM_HOTKEY) 或 theMsg.message = #WM_QUIT) ' 子程序WriteScreen和Int2Hex请看源码 WriteScreen (“ ” + Int2Hex (Count) + “ [The Thread is From ” + 取执行文件名 () + “ and Alt+L to Exit ”) Count = Count + 1 延时 (500) .判断循环尾 () ' 撤销热键 Api_UnregisterHotKey (0, HotKeyID) ' 释放全局原子 Api_DeleteAtom (HotKeyID) Api_Close Handle (FileMap) .如果真结束 返回 () (程序在Windows XP SP2 易语言4.02中测试通过) 通过本文的学习,是不是觉得易语言的确很简单?如果你是一个从没有写过程序的新手,可能会看得有点吃力,不过易语言程序附带非常详细的帮助说明和一些简单例程。等你熟悉了易语言后,再回头来看这篇文章就会觉得我说的一点都没错。 |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|