DbgUiRemoteBreakin是ntdll提供的用于在目标进程中创建远线程后下软件断点的函数
当我们用OD附加调试时,CreateRemoteThread函数在目标程序中创建了一个远程线程,然后在远程线程中调用DbgUiRemoteBreakin函数,DbgUiRemoteBreakin内部调用了DbgBreakPoint函数,DbgBreakPoint函数内部下了一个int 3断点,触发异常让操作系统运行异常处理程序,然后操作系统把控制权交管给调试器
ps:DbgUiIssueRemoteBreakin内部调用了DbgUiRemoteBreakin
反调试
hook DbgUiRemoteBreakin 函数,在内部直接调用ExitProcess()退出程序,不让程序被调试器成功附加
这里我们使用创建TLS回调的方式hook DbgUiRemoteBreakin (记录一下TLS创建流程)
/// <summary>
///添加文件tls.c
/// </summary>
//tls.c start
#include <Windows.h>
#pragma data_seg(".tls")
#pragma comment(linker, "/INCLUDE:__tls_used")
int _tls_index = 0;
int _tls_start = 0;
int _tls_end = 0;
int __xl_a = 0;
int __xl_z = 0;
extern PIMAGE_TLS_CALLBACK tls_callbacktbl[];//TLS回调函数数组
IMAGE_TLS_DIRECTORY32 _tls_used =
{
(DWORD)&_tls_start,
(DWORD)&_tls_end,
(DWORD)&_tls_index,
(DWORD)tls_callbacktbl,
0,
0
};
#pragma data_seg(".tls$ZZZ")
#pragma data_seg(".CRT$XLA")
#pragma data_seg(".CRT$XLZ")
#pragma data_seg(".rdata$T")
//tls.c end
# include<stdio.h>
# include<windows.h>
void HookDbgUiRemoteBreakin();
/// <summary>
///定义TLS回调函数
/// </summary>
void NTAPI TLS_CALLBACK(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
switch (Reason)
{
case DLL_PROCESS_ATTACH:
HookDbgUiRemoteBreakin();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
}
/// <summary>
///注册TLS回调函数
/// </summary>
extern "C" PIMAGE_TLS_CALLBACK tls_callbacktbl[] = { TLS_CALLBACK, 0 };
int main()
{
static int i = 0;
while (1)
{
Sleep(1000);
printf("当前程序运行中. EIP 位置 等于 %d \r\n", i++);
}
return 0;
system("pause");
}
void HookDbgUiRemoteBreakin()
{
BYTE bBuffer[0x10] = { 0 };
DWORD dwBreakAddress; //DbgUiRemoteBreakin函数的地址
DWORD dwOldProtect;
DWORD dwNum;
dwBreakAddress = (DWORD)GetProcAddress(LoadLibrary(L"ntdll.dll"), "DbgUiRemoteBreakin");
if (dwBreakAddress == 0)
return ;
bBuffer[0] = 0xE9; //jmp指令字节码
*((DWORD*)(bBuffer + 1)) = (DWORD)ExitProcess - dwBreakAddress - 5; //ExitProcess函数偏移地址
VirtualProtect((LPVOID)dwBreakAddress, 0x10, PAGE_EXECUTE_READWRITE, &dwOldProtect);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)dwBreakAddress, bBuffer, 5, (SIZE_T*)&dwNum);
VirtualProtect((LPVOID)dwBreakAddress, 0x10, dwOldProtect, &dwOldProtect);
}
反反调试
对照着本进程自身加载的未被hook的系统模块来复原目标进程被hook的系统模块
关键代码实现
# include<Windows.h>
# include<Tlhelp32.h>
# include<stdio.h>
//==================================================================
//函数名: GetProcessPID
//功能:获取指定进程名的进程的PID
//输入参数1:IN,目标进程的名称
//返回值:获取成功返回PID,获取失败返回NULL
//==================================================================
DWORD GetProcessPID(PCWCHAR name)
{
//初始化进程快照
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
HANDLE hProcessSanp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //获得快照句柄
Process32First(hProcessSanp, &pe32); //获取第一个进程
do
{
if (wcscmp(name, pe32.szExeFile) == 0)
{
CloseHandle(hProcessSanp);//关闭快照句柄,避免内存泄漏
return pe32.th32ProcessID; //注意这里使用的权限
}
} while (Process32Next(hProcessSanp, &pe32));
CloseHandle(hProcessSanp);//关闭快照句柄,避免内存泄漏
return NULL;
}
//==================================================================
//函数名: GetModuleInfo
//功能:获取指定进程的对应模块名的模块的信息
//输入参数1:IN,目标进程的PID
//输入参数2:IN,指定模块的模块名
//输入参数3:OUT,模块的信息结构体 PMODULEENTRY32
//返回值:获取成功返回TRUE,获取失败返回FALSE
//注意:模块名区分大小写
//==================================================================
BOOL GetModuleInfo(DWORD pid, PCWCHAR ModuleName, OUT PMODULEENTRY32 pme32)
{
MODULEENTRY32 me32;
me32.dwSize = sizeof(MODULEENTRY32); //在使用这个结构前,先设置它的大小
HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); //HANDLE也是属于句柄,是通用句柄 ,HWND是窗口句柄
if (hModuleSnap == INVALID_HANDLE_VALUE) //INVALID_HANDLE_VALUE表示无效的句柄
{
printf("模块读取失败\n");
return FALSE;
}
BOOL bMore = Module32First(hModuleSnap, &me32); //获取第一个模块信息
if (wcscmp(ModuleName, me32.szModule) == 0)
{
//printf("module name=%ws\n", me32.szModule);
*pme32 = me32;
return TRUE;
}
while (bMore)
{
bMore = Module32Next(hModuleSnap, &me32);
if (wcscmp(ModuleName, me32.szModule) == 0)
{
//printf("module name=%ws\n", me32.szModule);
*pme32 = me32;
return TRUE;
}
}
return FALSE;
}
//==================================================================
//函数名: RecoverModule
//功能:恢复目标进程被hook的系统模块
//输入参数1:IN,目标进程的进程名
//输入参数2:IN,指定模块的模块名
//输入参数3:IN,被hook的位置与被hook模块的基地址的偏移
//输入参数4:IN,被hook的长度
//返回值:恢复成功返回TRUE,恢复失败返回FALSE
//==================================================================
BOOL RecoverModule(LPCWCHAR ExeName, LPCWCHAR ModuleName, DWORD Offset, DWORD Length)
{
//devenv.exe
DWORD ExePID = GetProcessPID(ExeName);
printf("ExePID=%d\n", ExePID);
if (ExePID == NULL)
{
printf("获取程序PID失败\n");
return FALSE;
}
MODULEENTRY32 me;
BOOL result = GetModuleInfo(ExePID, ModuleName, &me);
if (result == FALSE)
{
printf("获取模块信息失败\n");
return FALSE;
}
printf("module name=%ws\n", me.szModule);
HMODULE hModule = GetModuleHandle(ModuleName);
LONG StartPos = (LONG)hModule + (LONG)Offset;
HANDLE hExe = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ExePID);
if (result == 0)
{
printf("打开进程失败\n");
return FALSE;
}
char buffer[256];
DWORD NumberOfBytesRead;
DWORD NumberOfBytesWritten;
DWORD OldProtect;
result = ReadProcessMemory(GetCurrentProcess(), (PVOID)StartPos, buffer, Length, &NumberOfBytesRead);
printf("NumberOfBytesRead=%d\n", NumberOfBytesRead);
if (result == 0)
{
printf("读内存失败,错误信息=%d\n",GetLastError());
return FALSE;
}
result = VirtualProtectEx(hExe, (PVOID)StartPos, Length, PAGE_EXECUTE_READWRITE, &OldProtect);
if (result == 0)
{
printf("更改目标程序内存属性失败,错误信息=%d\n", GetLastError());
return FALSE;
}
result = WriteProcessMemory(hExe, (PVOID)StartPos, buffer, Length, &NumberOfBytesWritten);
if (result == 0)
{
printf("写内存失败\n");
return FALSE;
}
result = VirtualProtectEx(hExe, (PVOID)StartPos, Length, OldProtect, &OldProtect);
if (result == 0)
{
printf("恢复目标程序内存属性失败\n");
return FALSE;
}
CloseHandle(hExe);
return TRUE;
}