一, H 0 0 K:
不知道什么时候开始, 我不喜欢叫这些知识为技术, 喜欢把这些知识叫做思想. 技术只是实现思想的一种行为.
HOOK 这是一个很古老的话题, 我见过最早的帖子日期在 2004 年左右, 而我 2018 年才学习, 不变的是思想.
二, 用户层:
Windows 下用户层被泛指三环, 上层社会, 比较高端繁华的地方, 让人向往, 确实很棒.
三, 内核层:
Windows 下内核层泛指零环, 底层社会, 硬件, 驱动设备等等协调工作, 无时无刻输送着劳动成果, 他们没有华丽外表封装自己, 但是他们并不丑陋, 相反的是他们真的很 "伟大".
四, 聊聊那些 HOOK 先关的学问
那时候的人真聪明, 想出各种办法去绕过, 截获, 利用原本的轨道办一些自己事情, 简单说这就是我理解的 Hook.
为什么放上去一张很 Lol 的图呢? 这是我第一次理解, 见到 HOOK 的样子, 我仍然觉着他很舒适.
以前也总喜欢用工具画一些精美的图片, 现在除了写文档报告, 都是用这种草稿图, 感觉更为亲切.
Hook 是有目的, 有需求点, 才去截获某一个过程, 去实现自己的功能又不影响系统正常的运转.
Hook 手段满天飞, inlinehook,IAThook,IDThook,Objecthook,SSDThook, 很遗憾很遗憾, 除了新鲜感, 现在的学习完全体会不到那个年代的乐趣. 这是我接触 Hook 的感触.
inlinehook: jmp 跳转, 个人感觉跳转的水平决定你的代码的稳定性.
IAThook: 了解 PE 的人知道 IAT(import address table), 脱壳过程中被加密最频繁的地方, 开辟虚拟空间加密, 解密 IAT. 在 PE 头中扩展头里面最后一个数组 (数据目录表) 中的第 2 项 (下标 1) 与第 12 项 (下标 11) 都可以找到对应 IAT 的 RVA.
《Windows PE 权威指南中》导入表的结构又称双桥结构, 其中一条桥在加载到内存后就会被填充成 IAT(FirstThunk), 而通过寻找另一条桥 INT(OriginalFirstThunk)实现函数绑定. 所以单桥无法进行函数绑定. 其实 INT 与 IAT 在文件中指向的是同一个结构体_IMAGE_THUNK_DATA32 这种结构体, 但是加载到内存后会把 IAT 填充成正确的函数地址, 可执行程序才能在内存中找 IAT 调用正确的函数地址.
说道这里你应该明白什么是 IAT: 保存加载到内存后正确的函数地址数组. Iathook 就很明显了, 替换函数地址, 从而该进程或者线程调用函数的时候, 去 IAT 找到的是我们自己的函数地址即可.
笼统的说 inthook 分为三步, 如上图所示:
第一步, 你要知道应用程序在调用函数的时候会去找 IAT, 获取 IAT 中保存的地址, 知道了这些东西以后, 你知道去截获, 替换的是什么, 就是地址.
第二步, 我们找到 IAT, 一般情况下我们不去找 IAT, 我们只需要通过 GetProcAddress()或者直接用函数名 (函数名编译器看来就是地址) 就可以获取. 然后找到被应用程序调用的函数, 如何去找呢? 最简单的办法逆向看一看它调用了那些函数就行了.
第三步, 替换 IAT 表中原来函数地址, 替换前先保存原地址, 备份一下, 然后把自己函数地址填充上去即可.
那么替换这些能干一些什么事情呢? 看两张图
结束进程失败:
挂起进程失败:
360 进程用 procexp.exe 或则任务管理器无法结束无法挂起呢? 虽然不一定用 iathook, 但是用 iathook 是可以做到的.
上一篇博客写的双进程, 先挂起单个进程, 就可以破掉双进程. hook 可以保护你不被挂起, 不被结束, 比起双进程守护更为稳定实用, 同样复杂程度也要高很多.
改进后的双进程保护程序:
先来看看这个功能:
基于 mfc 窗口, 拖拽需要保护的可执行文件. 但是这个进程是可以被结束的, 利用线程回调检测被保护的进程状态, 如果被结束, 立刻新创建新的进程, 然后使用等待函数等待响应, 这样就不会一直循环影响系统性能.
这里没有用任何的 hook 知识, 用到了创建快照, 3 环的进程遍历, 以及创建线程回调. 线程回调好处不影响当前进程的操作流程, 而且暂停保护功能只需要将线程挂起即可, 开启保护功能只需要将该线程唤醒, 大大的节省了代码量及运行效率.
还是有一些学习价值, 所以把代码贴上来, 相关的地方都给了详细的注释, 这里不再啰嗦.
代码实现如下:
需要类中声明, 定义的变量及函数:
- // This Process True?
- BOOL IsCurrentProcessTrue();
- // CallbackFunctionHandle
- HANDLE m_Handle = NULL;
- // DriverObject
- HANDLE hDriver = INVALID_HANDLE_VALUE;
- // SavePid
- static CString g_PathValue;
- // GetProcessFileName
- static CString g_FilePath;
主要实现代码:
- // The CallBack Function
- DWORD WINAPI ThreadPrcCallBack(LPVOID lparameter)
- {
- ProcessProtectMaster *pDlg = (ProcessProtectMaster*)lparameter;
- STARTUPINFO g_si = {};
- PROCESS_INFORMATION g_pi = {};
- while (true)
- {
- // 如果遍历进程发现 Pid 为假(开启新的进程)
- if (!pDlg->IsCurrentProcessTrue())
- {
- CreateProcess(pDlg->g_FilePath, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &g_si, &g_pi);
- WaitForSingleObject(g_pi.hProcess, INFINITE);
- CloseHandle(g_pi.hProcess);
- CloseHandle(g_pi.hThread);
- }
- }
- return 1;
- }
- // Is Process True?
- BOOL ProcessProtectMaster::IsCurrentProcessTrue()
- {
- HANDLE hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- if (hProcess == INVALID_HANDLE_VALUE)
- {
- AfxMessageBox(L"创建快照失败");
- return FALSE;
- }
- PROCESSENTRY32W p_32 = { sizeof(PROCESSENTRY32W) };
- if (!Process32First(hProcess, &p_32))
- {
- AfxMessageBox(L"Process32First falure!");
- CloseHandle(hProcess);
- return FALSE;
- }
- do{
- // 通过进程句柄获取文件映像路径
- // GetProcessImageFileName(hProcess, Pathbuf, MAX_PATH);
- // 强转 CString 对比可以用 ==
- CString temp = p_32.szExeFile;
- // 如果检测到有被保护进程则返回为真
- if (g_PathValue == temp)
- return TRUE;
- } while (Process32Next(hProcess, &p_32));
- // 没有检测到被保护进程为假
- return FALSE;
- }
- // Start Process_Protect
- void ProcessProtectMaster::OnBnClickedButton1()
- {
- HANDLE hProcess = INVALID_HANDLE_VALUE;
- // 1. 接收输入的 PID
- UpdateData();
- // 1.1 把获取的 PID 保存在静态变量中(回调函数会用)
- g_FilePath = m_Edit;
- // 1.2 获取文件名
- int nPos = g_FilePath.ReverseFind(_T('\\'));
- g_PathValue = g_FilePath.Mid(nPos + 1);
- // 2. 判断当前进程是否有效
- if (!IsCurrentProcessTrue())
- {
- AfxMessageBox(L"Process Flase");
- return;
- }
- // 3. 显示开启保护进程
- m_ShowValue.SetWindowTextW(L"已开启保护");
- // 4. 保护当前 PID(一直检测 PID, 当进程被结束立即打开新的进程)
- m_Handle = CreateThread(NULL, NULL, ThreadPrcCallBack, (LPVOID)this, NULL, NULL);
- }
- // Stop Process_Protect
- void ProcessProtectMaster::OnBnClickedButton2()
- {
- // 取消保护
- DWORD nRet = TerminateThread(m_Handle, NULL);
- if (nRet)
- {
- AfxMessageBox(L"进程守护关闭");
- m_StatucShow = "未启用";
- // 关闭状态更新到窗口
- UpdateData(FALSE);
- }
- else
- // 获取回调线程的 pid
- // DWORD t_Pid = GetThreadId(m_Handle);
- // 可以 cmd Kill -9 pid
- AfxMessageBox(L"请重新尝试");
- }
- // The Response File Receive
- void ProcessProtectMaster::OnDropFiles(HDROP hDropInfo)
- {
- // 1. 获得拖拽数目
- int DropCount = DragQueryFile(hDropInfo, -1, NULL, 0);
- // 2. 保存获取的路径信息
- char wcStr[MAX_PATH] = {};
- CString str;
- for (int i = 0; i < DropCount; i++)
- {
- // 3. 记得清空字符串
- wcStr[0] = 0;
- // 4. 获取路径名
- DragQueryFileA(hDropInfo, i, wcStr, MAX_PATH);
- str = wcStr;
- }
- // 直接用控件绑定变量会出现缓冲区过小问题
- m_Edit = str;
- UpdateData(FALSE);
- // 5. 释放内存
- DragFinish(hDropInfo);
- CDialogEx::OnDropFiles(hDropInfo);
- }
关于这两个功能一个是 IAThook, 一个是 ssdthook. 一个三环, 一个零环, hook 思想是不分三环还是零环的, 零环也同样有 iathook,inlinehook, 他并不是三环的专属.
下次博客更新内容是与 Windows 内核一些东西, 顺带把 hook 源码讲解一下与大家分享.
HOOK 这是一种思想(附源码)
来源: http://www.bubuko.com/infodetail-2921265.html