網站首頁 編程語言 正文
一:背景
1.講故事
前段時間分析了一個dump,一頓操作之后,我希望用外力來阻止程序內部對某一個com組件的調用,對,就是想借助外力實現,如果用 windbg 的話,可以說非常輕松,但現實情況比較復雜,客戶機沒有windbg,也不想加入任何的手工配置,希望全自動化來處理。
真的很無理哈。。。不過這種無理要求花點心思還是可以實現的,方法就是用代碼將應用程序變成調試器?來實現自動化阻止,為了簡化操作,我們拿 C# 的?File.WriteAllText
?來舉個例子,讓我的調試器來截獲它的 content。
2. 測試案例
為了方便講述,創建一個 WPF 程序,在 button 事件中用?File.WriteAllText
?方法來寫日志,參考代碼如下:
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { System.IO.File.WriteAllText("C:\\1.txt", DateTime.Now.ToString()); } }
代碼非常簡單,點一下按鈕就寫一條時間日志,接下來分別用 WinDbg 和 自定義調試器 來截獲這個時間。
二:WinDbg 下的實現
1. 實現原理
要想截獲日志,需要知道這個鏈路的下游方法,比如:kernel32!WriteFile
,msdn 上的定義如下:
BOOL WriteFile( [in] HANDLE hFile, [in] LPCVOID lpBuffer, [in] DWORD nNumberOfBytesToWrite, [out, optional] LPDWORD lpNumberOfBytesWritten, [in, out, optional] LPOVERLAPPED lpOverlapped );
其中?lpBuffer
?存放的就是 content 信息,?nNumberOfBytesToWrite
?存放的是長度,有了這些基礎,就可以通過 bp 下斷點了。
0:007> bp kernel32!WriteFile 0:007> g Breakpoint 0 hit eax=0126a4e8 ebx=00000000 ecx=000004a0 edx=76663510 esi=0320eb6c edi=010feaa8 eip=76663510 esp=010fea24 ebp=010fea90 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202 KERNEL32!WriteFile: 76663510 ff2558106c76 jmp dword ptr [KERNEL32!_imp__WriteFile (766c1058)] ds:002b:766c1058={KERNELBASE!WriteFile (75ebd760)} 0:000> kb 3 # ChildEBP RetAddr Args to Child 00 010fea90 6a829fef 00000000 010feaa8 00000013 KERNEL32!WriteFile 01 010feab8 6a829f2c 010fead4 00000000 00000013 mscorlib_ni!System.IO.FileStream.WriteFileNative+0x6f 02 010feae0 6a829ec5 00000013 00000000 0320d69c mscorlib_ni!System.IO.FileStream.WriteCore+0x3c
因為?kernel32!WriteFile
?用的是 stdcall 協定,所以?lpBuffer
?變量在?esp+0x8
?的位置,?nNumberOfBytesToWrite
?變量在?esp+0xc
?的位置。
0:000> da poi(esp+8) 0320eb6c "2022/11/24 17:25:39" 0:000> dp esp+0xc L1 010fea30 00000013 0:000> ? poi(esp+0xc) Evaluate expression: 19 = 00000013
從卦中看,content 和 length 都出來了,非常完美,接下來看下如何自定義實現調試器。
三:自己實現一個調試器
1. 技術原理
要想自定義實現,需要打通這三塊。
如何給?kernel32!WriteFile
?下 bp 斷點
bp 的原理其實就是?int 3
?,簡而言之就是 windbg 會將?kernel32!WriteFile
?指令的首字節修改成機器碼?0xcc
,命中之后又將?0xcc
?撤銷掉。這一串邏輯是 windbg 內部自己實現的,接下來我們驗證下,將首字節直接改成?0xcc
?。
0:011> x kernel32!WriteFile 76663510 KERNEL32!WriteFile (_WriteFile@20) 0:011> db 76663510 L1 76663510 ff . 0:011> eb 76663510 cc 0:011> db 76663510 L1 76663510 cc .
從卦中看已修改成功,接下來直接點擊 WPF 窗體的 button 按鈕就會直接命中這里的?int 3
?實現中斷。
到了這一步后,可以在程序中使用?WriteProcessMemory
?恢復?WriteFile
?原始字節為?ff
。
如何讓 int 3 中斷給程序
剛才看到的是中斷給WinDbg,那怎么中斷給程序呢? 其實 Win32 API 中有一個叫?DebugActiveProcess
?函數可以讓宿主程序充當調試器,mdsn 中的描述如下:
如何讀寫 wpf 的內存和寄存器
只要獲取到了 wpf 程序的進程和線程句柄,可以用?WriteProcessMemory
?和?ReadProcessMemory
?讀寫內存,用?GetThreadContext
?和?SetThreadContext
?讀寫寄存器。
2. 代碼實現
思路和技術都搞清楚后,代碼落地就非常簡單了,參考如下:
// HookDebug.cpp : This file contains the 'main' function. Program execution begins and ends there. // #include <iostream> #include <Windows.h> LPVOID writefile_addr = NULL; CREATE_PROCESS_DEBUG_INFO cpdi; BYTE int3 = 0xCC; BYTE ff = 0; BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde) { writefile_addr = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "WriteFile"); memcpy(&cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO)); ReadProcessMemory(cpdi.hProcess, writefile_addr, &ff, sizeof(BYTE), NULL); WriteProcessMemory(cpdi.hProcess, writefile_addr, &int3, sizeof(BYTE), NULL); return TRUE; } BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde) { CONTEXT ctx; PBYTE lpBuffer = NULL; DWORD lpBufferStart, nNumberOfBytesToWrite; PEXCEPTION_RECORD pr = &pde->u.Exception.ExceptionRecord; //int3 斷點 if (pr->ExceptionCode == EXCEPTION_BREAKPOINT && writefile_addr == pr->ExceptionAddress) { //1. unhook,恢復 writefile 的 WriteProcessMemory(cpdi.hProcess, writefile_addr, &ff, sizeof(BYTE), NULL); //2. 獲取上下文 ctx.ContextFlags = CONTEXT_ALL; GetThreadContext(cpdi.hThread, &ctx); //3. 獲取 WriteFile 寫入的內容 ReadProcessMemory(cpdi.hProcess, (PVOID)(ctx.Esp + 0x8), &lpBufferStart, sizeof(DWORD), NULL); ReadProcessMemory(cpdi.hProcess, (PVOID)(ctx.Esp + 0xc), &nNumberOfBytesToWrite, sizeof(DWORD), NULL); //4. 分配緩沖區 lpBuffer = (PBYTE)calloc(nNumberOfBytesToWrite + 1, sizeof(BYTE)); //5. copy 數據到緩沖區中 ReadProcessMemory(cpdi.hProcess, (LPVOID)lpBufferStart, lpBuffer, nNumberOfBytesToWrite, NULL); printf("截獲的內容: %s \n", lpBuffer); //6. 重新修改 eip ,指向 writefile 開頭,寫回到線程上下文中 ctx.Eip = (DWORD)writefile_addr; SetThreadContext(cpdi.hThread, &ctx); //7. 繼續執行 ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE); Sleep(0); //8. 重新 hook WriteProcessMemory(cpdi.hProcess, writefile_addr, &int3, sizeof(BYTE), NULL); return TRUE; } return FALSE; } void loop() { DEBUG_EVENT de; while (WaitForDebugEvent(&de, INFINITE)) { //注入事件 if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) { OnCreateProcessDebugEvent(&de); } //異常事件 if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { if (OnExceptionDebugEvent(&de)) continue; } ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE); } } int main() { //程序日志 DWORD dwPID = 23264; if (!DebugActiveProcess(dwPID)) { printf("fail"); return 1; } loop(); return 0; }
代碼中的?dwPID
?是 WPF 程序的 PID,指定好之后把程序跑起來,點擊 button 按鈕觀察,截圖如下,非常完美。
三:總結
在無法安裝 windbg 的受限環境下,部署?HookDebug.exe
?就是我們的另一種選擇,而且完全自動化攔截,基本實現無人工干預。
原文鏈接:https://www.cnblogs.com/huangxincheng/p/16924078.html
相關推薦
- 2022-04-11 Docker部署SonarQube的詳細流程_docker
- 2022-04-19 asp.core?同時兼容JWT身份驗證和Cookies?身份驗證兩種模式(示例詳解)_實用技巧
- 2022-03-24 使用Redis如何設置永久有效_Redis
- 2022-12-10 Qt如何自定義滑動條_C 語言
- 2022-04-16 C語言數據結構之二叉鏈表創建二叉樹_C 語言
- 2022-04-05 本地文件提交到gitee上報錯的問題:error: src refspec master does
- 2022-10-16 Flask快速實現分頁效果示例_python
- 2022-06-01 Go中的gRPC入門教程詳解_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支