前言
有意思的。
环境搭建
首先需要C++的开发环境,下载MinGW安装程序,得到一个mingw-w64-install.exe。
运行之后发现网络问题导致在线安装失败,选择需要的版本,比如我就是Windowsx64,离线下载安装,网络问题好像还是很大,总归是能下载了。
下载完成后解压,并配置好环境变量,将bin下面的可执行文件加入path中。
执行gcc -v命令,可以看到安装成功了:
Using built-in specs.
...
Thread model: win32
gcc version 8.1.0 (x86_64-win32-seh-rev0, Built by MinGW-W64 project)
测试编译C++程序
新建一个hello.cpp文件,写一个hello world:
#include <iostream>
using namespace std;
int main() {
cout<<"hello world"<<endl;
return 0;
}
配置好g++编译器后编译运行,不过我的环境下似乎自动配置好了,新建终端并输入:
g++ .\hello.cpp
生成a.exe程序并运行,可以看到输出:
PS D:\Cpp> .\a.exe
hello world
关于函数调用栈
观摩一下一个函数最开始会干什么,将程序放到IDA中打开,看到main函数的前几个汇编代码:
.text:0000000000401550 push rbp
.text:0000000000401551 mov rbp, rsp
.text:0000000000401554 sub rsp, 20h
保存rbp寄存器,更新rbp寄存器并开拓栈空间用于保存局部变量。
也就是说,如果是x86环境,此时的栈中只有函数返回地址和函数参数,还没有rbp。
API钩取
然而我是x64环境,此时前四个参数被放在了寄存器中。
根据参考文章稍作修改,要注意的点比如寄存器名字、一些变量的类型要放大到适合64位系统以及ContextFlags要设置为CONTEXT_FULL来获取所有寄存器:
#include <iostream>
#include <windows.h>
#include <tchar.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <shlobj.h>
LPVOID g_pWriteFile = NULL;
CREATE_PROCESS_DEBUG_INFO g_cpdi;
BYTE g_chINT3 = 0xCC;
BYTE g_orgByte = 0;
BOOL CreateProcessDebugEvent(LPDEBUG_EVENT pde)
{
g_pWriteFile = (void*)GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");
memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
ReadProcessMemory(g_cpdi.hProcess, g_pWriteFile, &g_orgByte, sizeof(BYTE), NULL);
WriteProcessMemory(g_cpdi.hProcess, g_pWriteFile, &g_chINT3, sizeof(BYTE), NULL);
return TRUE;
}
BOOL ExceprtionDebugEvent(LPDEBUG_EVENT pde)
{
CONTEXT ctx;
PBYTE lpBuffer = NULL;
ULONG_PTR dwNumOfBytesToWrite, dwAddrOfBuffer;
DWORD i = 0;
PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;
if (per->ExceptionCode == EXCEPTION_BREAKPOINT)
{
if (g_pWriteFile == per->ExceptionAddress)
{
WriteProcessMemory(g_cpdi.hProcess, g_pWriteFile, &g_orgByte, sizeof(BYTE), NULL);
ctx.ContextFlags = CONTEXT_FULL;
GetThreadContext(g_cpdi.hThread, &ctx);
// ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Rsp + 0x10), &dwAddrOfBuffer, sizeof(DWORD), NULL);
// ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Rsp + 0x18), &dwNumofBytestTowrite, sizeof(DWORD), NULL);
dwAddrOfBuffer = ctx.Rdx;
dwNumOfBytesToWrite = ctx.R8;
lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite + 1);
memset(lpBuffer, 0, dwNumOfBytesToWrite + 1);
ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, lpBuffer, dwNumOfBytesToWrite, NULL);
printf("orignal string: %s\n", lpBuffer);
for (i = 0; i < dwNumOfBytesToWrite; i++)
{
if (lpBuffer[i] >= 0x61 && lpBuffer[i] <= 0x7A)
{
lpBuffer[i] -= 0x20;
}
}
printf("string after changing:%s\n", lpBuffer);
WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, lpBuffer, dwNumOfBytesToWrite, NULL);
free(lpBuffer);
ctx.Rip = (DWORD64)g_pWriteFile;
SetThreadContext(g_cpdi.hThread, &ctx);
ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);
Sleep(0);
WriteProcessMemory(g_cpdi.hProcess, g_pWriteFile, &g_chINT3, sizeof(BYTE), NULL);
return TRUE;
}
}
return FALSE;
}
void DebugLoop()
{
DEBUG_EVENT de;
DWORD dwContinueStatus;
while (WaitForDebugEvent(&de, INFINITE))
{
dwContinueStatus = DBG_CONTINUE;
if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
{
CreateProcessDebugEvent(&de);
}
else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
{
if (ExceprtionDebugEvent(&de))
continue;
}
else if (de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
{
break;
}
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
}
}
int main(int argc, char* argv[])
{
DWORD dwPID = 0;
dwPID = atoi(argv[1]);
if (!DebugActiveProcess(dwPID))
{
printf("DebugActiveProcess(%d) failed!!!\n""Error Code = %d\n", dwPID, GetLastError());
return -1;
}
printf("DebugActiveProcess(%d) successfully!!!\n", dwPID);
DebugLoop();
return 0;
}
打开一个记事本,并使用PowerShell运行该程序调试记事本进程:
.\a.exe 27276
开始调试后,往记事本中写入一些字符串并保存,可以看到调试程序捕获了这些字符串:
orignal string: twings
twings hello world
string after changing:TWINGS
TWINGS HELLO WORLD
关闭记事本后再打开,可以发现写入的字符串已经被改为了大写字母。
参考
wingw-w64安装时 the file has been downloaded incorrectly!
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!