当软件被调试时isDebugged的值为1,当软件没有被调试时isDebugged的值为0
反调试
# include<Windows.h>
# include<stdio.h>
BOOL CheckDebug()
{
BOOL isDebugged;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &isDebugged);
return isDebugged;
}
int main()
{
while (1)
{
if (CheckDebug())
{
MessageBox(0, L"软件正在被调试", 0, 0);
return 0;
}
printf("I am running...\n");
Sleep(600);
}
return 0;
}
反反调试
hook掉CheckRemoteDebuggerPresent对isDebugged的赋值
以下是CheckRemoteDebuggerPresent的汇编代码
76673310 > 8BFF mov edi,edi
76673312 55 push ebp
76673313 8BEC mov ebp,esp
76673315 51 push ecx ; CheckRem.0088A015
76673316 837D 08 00 cmp dword ptr ss:[ebp+0x8],0x0
7667331A 56 push esi
7667331B 74 36 je short KernelBa.76673353
7667331D 8B75 0C mov esi,dword ptr ss:[ebp+0xC] ; CheckRem.<ModuleEntryPoint>
76673320 85F6 test esi,esi
76673322 74 2F je short KernelBa.76673353
76673324 6A 00 push 0x0
76673326 6A 04 push 0x4
76673328 8D45 FC lea eax,dword ptr ss:[ebp-0x4]
7667332B 50 push eax
7667332C 6A 07 push 0x7
7667332E FF75 08 push dword ptr ss:[ebp+0x8] ; CheckRem.<ModuleEntryPoint>
76673331 FF15 F0A26976 call dword ptr ds:[<&ntdll.NtQueryInform>; ntdll.ZwQueryInformationProcess
76673337 85C0 test eax,eax
76673339 79 09 jns short KernelBa.76673344
7667333B 8BC8 mov ecx,eax
7667333D E8 7EF0F4FF call KernelBa.765C23C0
76673342 EB 17 jmp short KernelBa.7667335B
76673344 33C0 xor eax,eax
76673346 3945 FC cmp dword ptr ss:[ebp-0x4],eax
76673349 0f95c0 setne al
7667334C 8906 mov dword ptr ds:[esi],eax -------------hook处
7667334E 33C0 xor eax,eax
76673350 40 inc eax
76673351 EB 0A jmp short KernelBa.7667335D
76673353 6A 57 push 0x57
76673355 FF15 C8A06976 call dword ptr ds:[<&ntdll.RtlSetLastWin>; ntdll.RtlSetLastWin32Error
7667335B 33C0 xor eax,eax
7667335D 5E pop esi ; CheckRem.007C356E
7667335E C9 leave
7667335F C2 0800 retn 0x8
将mov dword ptr ds:[esi],eax 更改为 mov dword ptr ds:[esi],0,即89 06 33 c0 40 eb 0a更改为c7 06 00 00 00 00 90,其结果是让 CheckRemoteDebuggerPresent一直对isDebugged赋值0
关键代码实现
# 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;
}
//==================================================================
//函数名: HookModule
//功能:hook模块
//输入参数1:IN,目标进程的进程名
//输入参数2:IN,指定模块的模块名
//输入参数3:IN,想要hook的位置与想要hook的模块的基地址的偏移
//输入参数4:IN,hook代码
//输入参数5:IN,hook代码的长度
//返回值:hook成功返回TRUE,hook失败返回FALSE
//==================================================================
BOOL HookModule(LPCWCHAR ExeName, LPCWCHAR ModuleName,DWORD Offset, char* code, 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;
}
DWORD NumberOfBytesRead;
DWORD NumberOfBytesWritten;
DWORD OldProtect;
result = VirtualProtectEx(hExe, (PVOID)StartPos, Length, PAGE_EXECUTE_READWRITE, &OldProtect);
if (result == 0)
{
printf("更改目标程序内存属性失败,错误信息=%d\n", GetLastError());
return FALSE;
}
result = WriteProcessMemory(hExe, (PVOID)StartPos, code, 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;
}