原帖及测试程序的地址:
【原创】反断点实例
本文我又发到我的CSDN博客里去了,还没审核通过
首先声明,理解本文需要对用户态的调试器原理有所了解,否则可能有些内容会不理解。
先来个测试程序,本文以分析和理解此程序为主。
测试程序下载
无论是逆向分析、还是脱壳破解,都离不开调试器。而windows下面用户态调试器最常用的,那就是OllyDbg了。
现在就用OD载入并调试该程序,如下图所示。

OD载入程序后,就停在了OEP(原始入口点)。
可能有经验的朋友观察入口代码,就会发现,该程序的入口是典型的VC++。但是不要着急
,慢慢来。
现在F9运行程序,程序是控制台界面的。如下图所示:

提示下个断点,随便找个地方按F2下断点就行(不要下在INT 3处),我就下在OEP了。

刚下完断点,就被中断了,一般程序是不可能,因为入口点一般只调用一次。
现在F7单步一下试试,如下图:

单步一下,程序居然直接崩了,很奇怪吧。好了,就调试到这里。
现在看TRE.exe的源码来讲解。
#include <SDKDDKVer.h>
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <process.h>
#pragma comment(linker, "/ENTRY:MyEntry")
#define ONLY_ASM __declspec(naked)
#ifdef __cplusplus
extern "C"{
#endif
#ifdef UNICODE
int wmainCRTStartup(
#else
int mainCRTStartup(
#endif
void);
#ifdef __cplusplus
}
#endif
#ifdef UNICODE
#define _tmainCRTStartup wmainCRTStartup
#else
#define _tmainCRTStartup mainCRTStartup
#endif // UNICODE
typedef VOID (NTAPI * Func_RtlRaiseException)(__in PEXCEPTION_RECORD ExceptionRecord);
typedef NTSTATUS (NTAPI * Func_NtRaiseException)(__in PEXCEPTION_RECORD ExceptionRecord, __in PCONTEXT ContextRecord, __in BOOLEAN FirstChance);
int _tmain(int argc, _TCHAR* argv[]);
DWORD WINAPI ProtectFunc(void * lParam);
DWORD tid = 0;
DWORD dwtmp0;
DWORD dwtmp1;
Func_RtlRaiseException f_rre;
Func_NtRaiseException f_nre;
DWORD dwtarr[12];
char fName[17] =
{
0x82, 0xb8, 0x9e, 0xad, 0xa5, 0xbf, 0xa9, 0x89, 0xb4,
0xaf, 0xa9, 0xbc, 0xb8, 0xa5, 0xa3, 0xa2, 0X00
};
PVOID GetSectionAddrByName(HMODULE hMod, PCHAR pSecName, DWORD * pSecSize)
{
if (!hMod || !pSecName) return NULL;
PIMAGE_DOS_HEADER pDos;
PIMAGE_NT_HEADERS32 pNtHeader;
PIMAGE_SECTION_HEADER pSection;
WORD sNum;
WORD i;
pDos = (PIMAGE_DOS_HEADER)hMod;
if (pDos->e_magic != IMAGE_DOS_SIGNATURE) return NULL;
pNtHeader = (PIMAGE_NT_HEADERS32)((UINT)hMod + pDos->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) return NULL;
pSection = (PIMAGE_SECTION_HEADER)((UINT)pNtHeader + 0x18 + pNtHeader->FileHeader.SizeOfOptionalHeader);
sNum = pNtHeader->FileHeader.NumberOfSections;
for (i = 0; i < sNum; i++)
{
if (strcmp((PCHAR)pSection[i].Name, pSecName) == 0)
{
*pSecSize = pSection[i].Misc.VirtualSize;
return (PVOID)((UINT)hMod + pSection[i].VirtualAddress);
}
}
return NULL;
}
ONLY_ASM INT_PTR WINAPI GetFuncAddr(INT_PTR hMod, const PCHAR pName)
{
_asm
{
push ebp
mov ebp, esp
sub esp, 0x10 //为局部变量开辟空间
push ebx
push esi
push edi
mov ebx, [ebp + 0x08]
mov eax, [ebx + 0x3c] //dosheader->e_lfanew
mov eax, [ebx + eax + 0x78] //导出表地址
test eax, eax //判断导出表地址是否为空
je ReturnNull
add eax, ebx //加模块基址
//取出输出表中一些有用的值
mov ebx, [eax + 0x18]
mov[ebp - 0x04], ebx
mov ebx, [eax + 0x1C]
add ebx, [ebp + 0x08]
mov[ebp - 0x08], ebx
mov ebx, [eax + 0x20]
add ebx, [ebp + 0x08]
mov[ebp - 0x0C], ebx
mov ebx, [eax + 0x24]
add ebx, [ebp + 0x08]
mov[ebp - 0x10], ebx
mov esi, [ebp + 0x0C]
test esi, 0xFFFF0000
jne Get_API_AddressByName
mov eax, esi
dec eax
jmp Get_API_AddressByIndex
//函数名取地址
Get_API_AddressByName :
xor eax, eax
mov edi, [ebp - 0x0C]
mov ecx, [ebp - 0x04]
LoopNumberOfName :
mov esi, [ebp + 0x0C]
push eax
mov ebx, [edi]
add ebx, [ebp + 0x08]
Match_API :
mov al, byte ptr[ebx]
cmp al, [esi]
jnz Not_Match
or al, 0x00
jz Get_API_Index_Found
inc ebx
inc esi
jmp Match_API
Not_Match :
pop eax
inc eax
add edi, 0x04
loop LoopNumberOfName
jmp ReturnNull
Get_API_Index_Found :
pop eax
Get_API_AddressByIndex :
mov ebx, [ebp - 0x10]
movzx eax, word ptr[ebx + eax * 0x02]
imul eax, 0x04
add eax, [ebp - 0x08]
mov eax, [eax]
add eax, [ebp + 0x08]
jmp ReturnVal
ReturnNull :
xor eax, eax
ReturnVal :
pop edi
pop esi
pop ebx
add esp, 0x10
pop ebp
retn 0x08
}
}
void MyInit1()
{
INT_PTR NtBase = 0;
PCHAR pc = fName;
__asm mov eax, fs:[0x30]
__asm mov eax, [eax + 0x0C]
__asm mov eax, [eax + 0x1C]
__asm mov eax, [eax + 0x08]
__asm mov NtBase, eax
f_nre = (Func_NtRaiseException)GetFuncAddr(NtBase, fName);
while (*pc)
{
*pc ^= 0xCC;
pc++;
}
CreateThread(NULL, 4194304, ProtectFunc, MyInit1, 0, &tid);
if (!tid || !f_nre)
{
printf("初始化失败!\n");
ExitProcess(0);
}
}
void MyInit()
{
HMODULE hMod = GetModuleHandle(NULL);
PCHAR pc = fName;
PIMAGE_DOS_HEADER pDos;
PIMAGE_NT_HEADERS32 pNtHeader;
pDos = (PIMAGE_DOS_HEADER)hMod;
while (*pc)
{
*pc ^= 0xCC;
pc++;
}
MyInit1();
if (pDos->e_magic != IMAGE_DOS_SIGNATURE) return;
pNtHeader = (PIMAGE_NT_HEADERS32)((UINT)hMod + pDos->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) return;
return;
}
ONLY_ASM int CallCrt()
{
__asm call _tmainCRTStartup
/* 下面的指令就是混淆视线的 */
__asm retn
__asm nop
__asm push ebp
__asm mov ebp, esp
__asm int 3
__asm int 1
__asm mov esp, ebp
__asm pop ebp
__asm retn
}
ONLY_ASM int MyEntry()
{
__asm
{
call MyInit
jmp CallCrt
/* 下面的指令就是混淆视线的 */
push ebp
mov ebp, esp
call IsDebuggerPresent
push 1
mov dwtmp0, eax
call _tmain
push dword ptr [ebp + 0x08]
call ProtectFunc
cmp dwtmp1, 0
pop ecx
pop ecx
jnz JMP0
push 0x1
call _tmain
pop ecx
JMP0 :
push 0xC0000409
call ProtectFunc
pop ecx
pop ebp
retn
push ebp
mov ebp, esp
push 0x17
call IsDebuggerPresent
test eax, eax
je JMP0
push 0x2
pop ecx
mov dwtarr[0], eax
mov dwtarr[1], ecx
mov dwtarr[2], edx
mov dwtarr[3], ebx
mov dwtarr[4], esi
mov dwtarr[5], edi
mov word ptr dwtarr[6], ss
mov word ptr dwtarr[7], cs
mov word ptr dwtarr[8], ds
mov word ptr dwtarr[9], es
mov word ptr dwtarr[10], fs
mov word ptr dwtarr[11], gs
pushfd
push 0x4
call _tmain
pop eax
imul eax, eax, 0x0
mov dword ptr ss : [ebp + eax - 0x8], ecx
push 0x4
pop eax
mov esp, ebp
pop ebp
retn
}
}
PBYTE WINAPI CompBytes(PBYTE pm0, PBYTE pm1, UINT mMax, PBYTE pOByte)
{
UINT i;
for (i = 0; i < mMax;i++)
{
if (pm0[i] != pm1[i])
{
if (pm0[i] == 0xCC)
{
*pOByte = pm1[i];
return &pm0[i];
}
}
}
return NULL;
}
DWORD WINAPI ProtectFunc(void * lParam)
{
PBYTE poMem;
PBYTE oAddr;
DWORD mSize;
PBYTE nAddr;
NTSTATUS ns;
EXCEPTION_RECORD ER;
CONTEXT ct;
BYTE OByte;
oAddr = (PBYTE)GetSectionAddrByName(GetModuleHandle(NULL), ".text", &mSize);
poMem = (PBYTE)HeapAlloc(GetProcessHeap(), 0, mSize);
if (!poMem)
{
printf("HeapAlloc失败!退出...\n");
ExitProcess(0);
}
memcpy_s(poMem, mSize, oAddr, mSize);
while (true)
{
nAddr = CompBytes(oAddr, poMem, mSize, &OByte);
if (nAddr)
{
while (*nAddr != OByte)
{
/* 抛出假断点异常 */
ER.ExceptionCode = EXCEPTION_BREAKPOINT;
ER.ExceptionFlags = 0;
ER.ExceptionRecord = NULL;
ER.ExceptionAddress = nAddr;
ER.NumberParameters = 1;
ER.ExceptionInformation[0] = 0;
ct.ContextFlags = CONTEXT_FULL;
ct.Eip = DWORD(nAddr);
ct.Dr0 = 0;
ct.Dr1 = 0;
ct.Dr2 = 0;
ct.Dr3 = 0;
ct.Dr6 = 0;
ct.Dr7 = 0;
ct.SegGs = 0x2B;
ct.SegFs = 0x53;
ct.SegEs = 0x2B;
ct.SegDs = 0x2B;
ct.SegCs = 0x23;
ct.EFlags = 0x00000246;
ct.SegSs = 0x2B;
ns = f_nre(&ER, &ct, FALSE);
Sleep(60);
}
}
Sleep(80);
}
HeapFree(GetProcessHeap(), 0, poMem);
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
printf("下个断点试试!:)\n");
getchar();
printf("按任意键退出!\n");
getchar();
return 0;
}
void MyInit()
{
HMODULE hMod = GetModuleHandle(NULL);
PCHAR pc = fName;
PIMAGE_DOS_HEADER pDos;
PIMAGE_NT_HEADERS32 pNtHeader;
pDos = (PIMAGE_DOS_HEADER)hMod;
while (*pc)
{
*pc ^= 0xCC;
pc++;
}
MyInit1();
if (pDos->e_magic != IMAGE_DOS_SIGNATURE) return;
pNtHeader = (PIMAGE_NT_HEADERS32)((UINT)hMod + pDos->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) return;
return;
}
[培训]科锐逆向工程师培训第53期2025年7月8日开班!