void Function() { Input++; Output++; Input+=Output; printf("函数结果:%d,%d",Input,Output); } 反汇编代码: IDA: .text:00401096 rep stosd .text:00401098 mov eax, Input .text:0040109D add eax, 1 .text:004010A0 mov Input, eax .text:004010A5 mov ecx, Output .text:004010AB add ecx, 1 .text:004010AE mov Output, ecx .text:004010B4 mov edx, Input .text:004010BA add edx, Output .text:004010C0 mov Input, edx .text:004010C6 mov eax, Output .text:004010CB push eax .text:004010CC mov ecx, Input .text:004010D2 push ecx .text:004010D3 push offset s_PSDD ; "函数结果:%d,%d" .text:004010D8 call printf
(2)简单加花后,在IDA中反汇编错误,在w32dasm中反汇编错误,但是在OD中反汇编正确
加花后程序:
void Function() { _asm jz label _asm jnz label _asm __emit 0e8h //e8是call指令 label: Input++; Output++; Input+=Output; printf("函数结果:%d,%d",Input,Output); } 在OD中: 在IDA中: .text:00401096 rep stosd .text:00401098 jz short near ptr loc_40109C+1 .text:00401098 .text:0040109A jnz short near ptr loc_40109C+1 .text:0040109A .text:0040109C .text:0040109C loc_40109C: ; CODE XREF: .text:00401098 j .text:0040109C ; .text:0040109A j .text:0040109C call near ptr 4275D142h .text:004010A1 add [ebx-3F5CFE40h], al .text:004010A7 xor eax, 0D8B0042h .text:004010AC mov esp, 83004235h .text:004010B1 rol dword ptr [ecx], 89h .text:004010B4 or eax, offset Output .text:004010B9 mov edx, Input .text:004010BF add edx, Output .text:004010C5 mov Input, edx .text:004010CB mov eax, Output .text:004010D0 push eax .text:004010D1 mov ecx, Input .text:004010D7 push ecx .text:004010D8 push offset s_PSDD ; "函数结果:%d,%d" .text:004010DD call printf
(3)改进,加花后,IDA与OD均反汇编错误
改进后程序:
void Function() { _asm xor eax,eax _asm test eax,eax _asm jz label1 _asm jnz label0 label0: _asm __emit 0e8h label1: Input++; Output++; Input+=Output; printf("函数结果:%d,%d",Input,Output); } OD中: IDA中: .text:0040109C jz short near ptr loc_4010A0+1 .text:0040109C .text:0040109E jnz short $+2 .text:004010A0 .text:004010A0 loc_4010A0: ; CODE XREF: .text:0040109C j .text:004010A0 call near ptr 4275D146h .text:004010A5 add [ebx-3F5CFE40h], al .text:004010AB xor eax, 0D8B0042h .text:004010B0 mov esp, 83004235h .text:004010B5 rol dword ptr [ecx], 89h .text:004010B8 or eax, offset Output .text:004010BD mov edx, Input .text:004010C3 add edx, Output .text:004010C9 mov Input, edx .text:004010CF mov eax, Output .text:004010D4 push eax .text:004010D5 mov ecx, Input .text:004010DB push ecx .text:004010DC push offset s_PSDD ; "函数结果:%d,%d" .text:004010E1 call printf
应用
(1) 简单加花
实现简单,但是原理比较简单,高手很容易就能去除,一般以消耗攻击者的耐性来达到目的。
(2)复杂加花
类似于加壳
1. 记录程序的原入口点,
2. 找到PE文件的空白区域,在空白区域内写入花指令(或者添加新节)
3. 把入口点地址改为新入口地址
4. 花指令执行完后跳转到原入口点地址
在程序执行时,程序将从新的入口地址执行,即花指令先被执行,然后再执行程序原来的入口地址功能。
增加了静态分析的难度,提高了代码的信息隐藏效果,该方法一般应用于病毒的免杀中。
2. 隐藏API
逆向分析工作人员往往就是通过API在极短时间内获取了大量信息,从而使他们成功定位目标程序的关键代码段。所以隐藏对API 的调用可以有效地提高程序的抗分析能力。
例如一个简单的程序:
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MessageBox(NULL,"test","box",0); return 0; } 反汇编代码如下: .text:00401028 mov esi, esp .text:0040102A push 0 ; uType .text:0040102C push offset Caption ; "Test" .text:00401031 push offset Text ; "Reverse Me" .text:00401036 push 0 ; hWnd .text:00401038 call ds:MessageBoxA(x,x,x,x)
目的是让反汇编的代码看不到 ds: MessageBoxA(x,x,x,x) 这样的提示。
1.最基本、最简单的方法:
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { /******************************************************** 1. 定义字符串 ********************************************************/ TCHAR MsgBoxA[MAX_PATH]="MessageBoxA"; /******************************************************** 2. 获取MessageBoxA的函数地址 ********************************************************/ HMODULE hMod=LoadLibrary("user32.dll"); MYFUNC func=(MYFUNC)GetProcAddress(hMod,MsgBoxA);//获取MessageBoxA的函数地址。 func(0,"Reverse Me","Test",0); //调用MessageBoxA函数。 FreeLibrary(hMod); return 0; } IDA中(V5.4) .text:0040102E mov eax, dword ptr ds:aMessageboxa ; "MessageBoxA" .text:00401033 mov dword ptr [ebp+ProcName], eax .text:00401039 mov ecx, dword ptr ds:aMessageboxa+4 .text:0040103F mov [ebp+var_100], ecx .text:00401045 mov edx, dword ptr ds:aMessageboxa+8 .text:0040104B mov [ebp+var_FC], edx .text:00401051 mov ecx, 3Eh .text:00401056 xor eax, eax .text:00401058 lea edi, [ebp+var_F8] .text:0040105E rep stosd .text:00401060 mov esi, esp .text:00401062 push offset LibFileName ; "user32.dll" .text:00401067 call ds:__imp__LoadLibraryA@4 ; LoadLibraryA(x) .text:0040106D cmp esi, esp .text:0040106F call __chkesp .text:00401074 mov [ebp+hLibModule], eax .text:0040107A mov esi, esp .text:0040107C lea eax, [ebp+ProcName] .text:00401082 push eax ; lpProcName .text:00401083 mov ecx, [ebp+hLibModule] .text:00401089 push ecx ; hModule .text:0040108A call ds:__imp__GetProcAddress@8 ; GetProcAddress(x,x) .text:00401090 cmp esi, esp .text:00401092 call __chkesp .text:00401097 mov [ebp+var_10C], eax .text:0040109D mov esi, esp .text:0040109F push 0 .text:004010A1 push offset aTest ; "Test" .text:004010A6 push offset aReverseMe ; "Reverse Me" .text:004010AB push 0 .text:004010AD call [ebp+var_10C] OD中:
3. 进行简单加密处理
隐藏字符串"MessageBoxA","Reverse Me","Test"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { /******************************************************** 1. 加密字符串 ********************************************************/ char MsgBoxA[]={0x5c,0x74,0x62,0x62,0x70,0x76,0x74,0x53,0x7e,0x69,0x50,0x00}; //字符串"MessageBoxA"的加密形式。 char lpText[]={0x43,0x74,0x67,0x74,0x63,0x62,0x74,0x31,0x5C,0x74,0x00}; //字符串"Reverse Me"的加密形式。 char lpCaption[]={0x45,0x74,0x62,0x65,0x00}; //字符串"Test"的加密形式。 /******************************************************** 2. 解密字符串 ********************************************************/ for(int i=0;i<strlen(MsgBoxA);i++) MsgBoxA[i]^=0x11; //解密字符串"MessageBoxA" for(i=0;i<strlen(lpText);i++) lpText[i]^=0x11; //解密字符串"Reverse Me" for(i=0;i<strlen(lpCaption);i++) lpCaption[i]^=0x11; //解密字符串"Test" /******************************************************** 3. 获取MessageBoxA的函数地址 ********************************************************/ HMODULE hMod=LoadLibrary("user32.dll"); if(hMod) { MYFUNC func=(MYFUNC)GetProcAddress(hMod,MsgBoxA); //获取MessageBoxA()的函数地址。 func(0,lpText,lpCaption,0); //调用MessageBoxA函数。 FreeLibrary(hMod); } return 0; } IDA中: .text:0040146E mov edx, [ebp+arg_8] .text:00401471 push edx .text:00401472 mov eax, [ebp+arg_4] .text:00401475 push eax .text:00401476 push offset s_SecondChanceA ; "Second Chance Assertion Failed: File %s"... .text:0040147B lea ecx, [ebp+OutputString] .text:00401481 push ecx .text:00401482 call dword_4235D0 OD中:
3.将GetProcAddress也隐藏
类似上面的方法
/******************************************************** 3. 获取MessageBoxA的函数地址 ********************************************************/ Decrypt((char * )404445,(char * )404543); __asm inc eax __asm dec eax HMODULE hMod=LoadLibrary("user32.dll"); if(hMod) { MYFUNC func=(MYFUNC)GetProcAddress(hMod,MsgBoxA); //获取MessageBoxA()的函数地址。 func(0,lpText,lpCaption,0); //调用MessageBoxA函数。 FreeLibrary(hMod); } __asm inc eax __asm dec eax
二.抗动态调试技术
动态调试,也就是使用调试器等工具对软件的运行进行跟踪,从而对关键代码进行逆向工程,或者干脆破坏软件中的保护措施,对软件进行盗版,抗动态调试即阻碍这个逆向过程。
反调试目的:主要针对OD等调试跟踪程序,检测内存或者进程中是否有调试软件运行,进而让这些程序失效或者直接失去响应。检测自身是否运行在调试器下。
1.调用Win32API检测程序是否处于调试状态
IsDebuggerPresentFlag()
// IsDebuggerPresentFlag // 返回值为true时检测到调试器,否则无调试器 /* bool IsDebuggerPresentFlag() { HINSTANCE hInst = LoadLibrary("kernel32.dll"); if(hInst!=NULL) { FARPROC pIsDebuggerPresent=GetProcAddress(hInst,"IsDebuggerPresent"); if(pIsDebuggerPresent!=NULL) return pIsDebuggerPresent(); } return FALSE; }
也可以自实现,
bool IsDebuggerPresentFlag() { __asm { mov eax, fs:[0x30] //在位于TEB偏移30h处获得PEB地址 movzx eax, byte ptr [eax+2] //获得PEB偏移2h处BeingDebugged的值 test eax, eax jne rt_label jmp rf_label } rt_label: return true; rf_label: return false; }
测试结果:该方法太古老,在OD中不能检测
NtGlobalFlags() // NtGlobalFlags,可检测出检测OllyDebug // 返回值为true时检测到调试器,否则无调试器 bool NtGlobalFlags() { __asm { mov eax, fs:[30h] mov eax, [eax+68h] and eax, 0x70 test eax, eax jne rt_label jmp rf_label } rt_label: return true; rf_label: return false; }
在OD中能检测,但是用OD的插件能够去掉(HideOD插件选上后不能检测,选上auto to run hideod 以及hideNtDebugBit)
HeapFlags() // HeapFlags // 返回值为true时检测到调试器,否则无调试器 bool HeapFlags() { __asm { mov eax, fs:[30h] mov eax, [eax+18h] ;PEB.ProcessHeap mov eax, [eax+0ch] ;PEB.ProcessHeap.ForceFlags cmp eax, 2 jne rt_label jmp rf_label } rt_label: return true; rf_label: return false; }
在OD中,能检测,但是用OD的插件能够去掉(HideOD插件选上后不能检测,选上auto to run hideod以及hideNtDebugBit)
RemoteDebuggerPresent() // RemoteDebuggerPresent // 返回值为true时检测到调试器,否则无调试器 typedef BOOL (WINAPI *CHECK_REMOTE_DENUGGER_PRESENT)(HANDLE,PBOOL); BOOL RemoteDebuggerPresent() { BOOL bDebuggerPresent=FALSE; CHECK_REMOTE_DENUGGER_PRESENT CheckRemoteDebuggerPresent; HMODULE hModule = GetModuleHandle("kernel32.dll"); if (hModule==INVALID_HANDLE_VALUE) { return FALSE; } CheckRemoteDebuggerPresent =(CHECK_REMOTE_DENUGGER_PRESENT)GetProcAddress(hModule, "CheckRemoteDebuggerPresent"); if( CheckRemoteDebuggerPresent(GetCurrentProcess(),&bDebuggerPresent)) { return bDebuggerPresent; } return FALSE; }
在OD中,能检测,但是用OD的插件能够去掉(hideOd插件选上后不能测试,选上CheckRemoteDebuggerPresent)