• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            S.l.e!ep.¢%

            像打了激速一樣,以四倍的速度運(yùn)轉(zhuǎn),開心的工作
            簡(jiǎn)單、開放、平等的公司文化;尊重個(gè)性、自由與個(gè)人價(jià)值;
            posts - 1098, comments - 335, trackbacks - 0, articles - 1
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            反調(diào)試技巧總結(jié)-原理和實(shí)現(xiàn) 收藏
            標(biāo) 題: 【原創(chuàng)】反調(diào)試技巧總結(jié)-原理和實(shí)現(xiàn)(1)(2)(3)(4)(5)(6)......
            作 者: shellwolf
            時(shí) 間: 2008-08-10,22:40
            鏈 接: http://bbs.pediy.com/showthread.php?t=70470

            反調(diào)試技巧總結(jié)-原理和實(shí)現(xiàn)
            -------------------------------------------------------------------------------------------------------
            2008.8.7? shellwolf
            一、 前言
            ??? 前段學(xué)習(xí)反調(diào)試和vc,寫了antidebug-tester,經(jīng)常會(huì)收到message希望交流或索要實(shí)現(xiàn)代碼,我都沒有回復(fù)。其實(shí)代碼已經(jīng)在編程版提供了1個(gè)版本,另其多是vc內(nèi)嵌asm寫的,對(duì)cracker而言,只要反下就知道了。我想代碼其實(shí)意義不是很大,重要的是理解和運(yùn)用。
            ??? 做個(gè)簡(jiǎn)單的總結(jié),說明下實(shí)現(xiàn)原理和實(shí)現(xiàn)方法。也算回復(fù)了那些給我發(fā)Message的朋友。

            ??? 部分代碼和參考資料來源:
            1、<<脫殼的藝術(shù)>> hawking
            2、<<windows anti-debugger reference>> Angeljyt
            3、http://bbs.pediy.com
            4、<<軟件加密技術(shù)內(nèi)幕>> 看雪學(xué)院
            5、<<ANTI-UNPACKER TRICKS>> Peter Ferrie

            我將反調(diào)試技巧按行為分為兩大類,一類為檢測(cè),另一類為攻擊,每類中按操作對(duì)象又分了五個(gè)小類:
            1、 通用調(diào)試器???? 包括所有調(diào)試器的通用檢測(cè)方法
            2、 特定調(diào)試器???? 包括OD、IDA等調(diào)試器,也包括相關(guān)插件,也包括虛擬環(huán)境
            3、 斷點(diǎn)?????????? 包括內(nèi)存斷點(diǎn)、普通斷點(diǎn)、硬件斷點(diǎn)檢測(cè)
            4、 單步和跟蹤???? 主要針對(duì)單步跟蹤調(diào)試
            5、 補(bǔ)丁?????????? 包括文件補(bǔ)丁和內(nèi)存補(bǔ)丁
            反調(diào)試函數(shù)前綴
            ????????????? 檢測(cè)??????? 攻擊
            通用調(diào)試器???? FD_??????? AD_
            特定調(diào)試器???? FS_??????? AS_
            斷點(diǎn)?????????? FB_??????? AB_
            單步和跟蹤???? FT_??????? AT_
            補(bǔ)丁?????????? FP_??????? AP_

            聲明:
            1、本文多數(shù)都是摘錄和翻譯,我只是重新組合并翻譯,不會(huì)有人告侵權(quán)吧。里面多是按自己的理解來說明,可能有理解錯(cuò)誤,或有更好的實(shí)現(xiàn)方法,希望大家?guī)兔χ赋鲥e(cuò)誤。
            2、我并沒有總結(jié)完全,上面的部分分類目前還只有很少的函數(shù)甚至空白,等待大家和我一起來完善和補(bǔ)充。我堅(jiān)信如果有扎實(shí)的基礎(chǔ)知識(shí),豐富的想像力,靈活的運(yùn)用,就會(huì)創(chuàng)造出更多的屬于自己的反調(diào)試。而最強(qiáng)的反調(diào)試,通常都是自己創(chuàng)造的,而不是來自別人的代碼。

            二、 查找-通用調(diào)試器(FD_)
            函數(shù)列表如下,后面會(huì)依次說明,需事先說明的是,這些反調(diào)試手段多數(shù)已家喻戶曉,目前有效的不多,多數(shù)已可以通過OD的插件順利通過,如果你想驗(yàn)證它們的有效性,請(qǐng)關(guān)閉OD的所有反反調(diào)試插件:
            bool FD_IsDebuggerPresent();
            bool FD_PEB_BeingDebuggedFlag();
            bool FD_PEB_NtGlobalFlags();
            bool FD_Heap_HeapFlags();
            bool FD_Heap_ForceFlags();
            bool FD_Heap_Tail();
            bool FD_CheckRemoteDebuggerPresent();
            bool FD_NtQueryInfoProc_DbgPort();
            bool FD_NtQueryInfoProc_DbgObjHandle();
            bool FD_NtQueryInfoProc_DbgFlags();
            bool FD_NtQueryInfoProc_SysKrlDbgInfo();
            bool FD_SeDebugPrivilege();
            bool FD_Parent_Process();
            bool FD_DebugObject_NtQueryObject();
            bool FD_Find_Debugger_Window();
            bool FD_Find_Debugger_Process();
            bool FD_Find_Device_Driver();
            bool FD_Exception_Closehandle();
            bool FD_Exception_Int3();
            bool FD_Exception_Popf();
            bool FD_OutputDebugString();
            bool FD_TEB_check_in_Vista();
            bool FD_check_StartupInfo();
            bool FD_Parent_Process1();
            bool FD_Exception_Instruction_count();
            bool FD_INT_2d();

            2.1 FD_IsDebuggerPresent()
            對(duì)調(diào)試器來說,IsDebuggerPresent是臭名昭著的惡意函數(shù)。不多說了,它是個(gè)檢測(cè)調(diào)試的api函數(shù)。實(shí)現(xiàn)更簡(jiǎn)單,只要調(diào)用IsDebuggerPresent就可以了。在調(diào)用它之前,可以加如下代碼,以用來檢測(cè)是否在函數(shù)頭有普通斷點(diǎn),或是否被鉤掛。
            ? //check softbreak
            ? if(*(BYTE*)Func_addr==0xcc)
            ??? return true;
            ? //check hook
            ? if(*(BYTE*)Func_addr!=0x64)
            ??? return true;
            2.2 FD_PEB_BeingDebuggedFlag
            我們知道,如果程序處于調(diào)試器中,那么在PEB結(jié)構(gòu)中有個(gè)beingDegug標(biāo)志會(huì)被設(shè)置,直接讀取它就可判斷是否在調(diào)試器中。實(shí)際上IsDebuggerPresent就是這么干的。
            ? __asm
            ? {
            ??? mov eax, fs:[30h] ;EAX =? TEB.ProcessEnvironmentBlock
            ??? inc eax
            ??? inc eax
            ??? mov eax, [eax]
            ??? and eax,0x000000ff? ;AL? =? PEB.BeingDebugged
            ??? test eax, eax
            ??? jne rt_label
            ??? jmp rf_label
            ? }

            2.3 FD_PEB_NtGlobalFlags
            PEB中還有其它FLAG表明了調(diào)試器的存在,如NtGlobalFlags。它位于PEB環(huán)境中偏移為0x68的位置,默認(rèn)情況下該值為0,在win2k和其后的windows平臺(tái)下,如果在調(diào)試中,它會(huì)被設(shè)置為一個(gè)特定的值。使用該標(biāo)志來判斷是否被調(diào)試并不可靠(如在winnt中),但這種方法卻也很常用。這個(gè)標(biāo)志由下面幾個(gè)標(biāo)志組成:
            FLG_HEAP_ENABLE_TAIL_CHECK (0x10)
            FLG_HEAP_ENABLE_FREE_CHECK (0x20)
            FLG_HEAP_VALIDATE_PARAMETERS (0x40)
            檢測(cè)NtGlobalFlags的方法如下,這個(gè)方法在ExeCryptor中使用過。
            __asm
            ? {
            ??? mov eax, fs:[30h]
            ??? mov eax, [eax+68h]
            ??? and eax, 0x70
            ??? test eax, eax
            ??? jne rt_label
            ??? jmp rf_label
            ? }

            2.4 FD_Heap_HeapFlags()
            同樣,調(diào)試器也會(huì)在堆中留下痕跡,你可以使用kernel32_GetProcessHeap()函數(shù),如果你不希望使用api函數(shù)(以免暴露),則可以直接在PEB中尋找。同樣的,使用HeapFlags和后面提到的ForceFlags來檢測(cè)調(diào)試器也不是非常可靠,但卻很常用。
            這個(gè)域由一組標(biāo)志組成,正常情況下,該值應(yīng)為2。
            ? __asm
            ? {
            ??? mov eax, fs:[30h]
            ??? mov eax, [eax+18h] ;PEB.ProcessHeap
            ??? mov eax, [eax+0ch] ;PEB.ProcessHeap.Flags
            ??? cmp eax, 2
            ??? jne rt_label
            ??? jmp rf_label
            ? }

            2.5 FD_Heap_ForceFlags
            進(jìn)程堆里另外一個(gè)標(biāo)志,F(xiàn)orceFlags,它也由一組標(biāo)志組成,正常情況下,該值應(yīng)為0。
            ? __asm
            ? {
            ??? mov eax, fs:[30h]
            ??? mov eax, [eax+18h] ;PEB.ProcessHeap
            ??? mov eax, [eax+10h] ;PEB.ProcessHeap.ForceFlags
            ??? test eax, eax
            ??? jne rt_label
            ??? jmp rf_label
            ? }

            2.6 FD_Heap_Tail
            如果處于調(diào)試中,堆尾部也會(huì)留下痕跡。標(biāo)志HEAP_TAIL_CHECKING_ENABLED 將會(huì)在分配的堆塊尾部生成兩個(gè)0xABABABAB。如果需要額外的字節(jié)來填充堆尾,HEAP_FREE_CHECKING_ENABLED標(biāo)志則會(huì)生成0xFEEEFEEE。

            據(jù)說Themida使用過這個(gè)反調(diào)試
            ? __asm
            ? {
            ??? mov eax, buff
            ??? ;get unused_bytes
            ??? movzx ecx, byte ptr [eax-2]
            ??? movzx edx, word ptr [eax-8] ;size
            ??? sub eax, ecx
            ??? lea edi, [edx*8+eax]
            ??? mov al, 0abh
            ??? mov cl, 8
            ??? repe sca**
            ??? je rt_label
            ??? jmp rf_label
            ? }

            2.7 FD_CheckRemoteDebuggerPresent
            CheckRemoteDebuggerPresent是另一個(gè)檢測(cè)調(diào)試的api,只是可惜它似乎只能在winxp sp1版本以后使用。它主要是用來查詢一個(gè)在winnt時(shí)就有的一個(gè)數(shù)值,其內(nèi)部會(huì)調(diào)用NtQueryInformationProcess(),我是這樣實(shí)現(xiàn)的:
            ? FARPROC Func_addr ;
            ? HMODULE hModule = GetModuleHandle("kernel32.dll");
            ? if (hModule==INVALID_HANDLE_VALUE)
            ??? return false;
            ? (FARPROC&) Func_addr =GetProcAddress(hModule, "CheckRemoteDebuggerPresent");
            ? if (Func_addr != NULL)
            ? {
            ??? __asm
            ??? {
            ????? push? eax;
            ????? push? esp;
            ????? push? 0xffffffff;
            ????? call? Func_addr;
            ????? test? eax,eax;
            ????? je??? rf_label;
            ????? pop??? eax;
            ????? test? eax,eax
            ????? je??? rf_label;
            ????? jmp??? rt_label;
            ??? }
            ? }

            2.8 FD_NtQueryInfoProc_DbgPort
            使用ntdll_NtQueryInformationProcess()來查詢ProcessDebugPort可以用來檢測(cè)反調(diào)試。如果進(jìn)程被調(diào)試,其返回值應(yīng)為0xffffffff。
            下面的代碼應(yīng)該是從pediy里copy過來的,時(shí)間太長(zhǎng),不記得是哪位兄弟的代碼了。
            ? HMODULE hModule = GetModuleHandle("ntdll.dll");
            ? ZW_QUERY_INFORMATION_PROCESS ZwQueryInformationProcess;
            ??? ZwQueryInformationProcess = (ZW_QUERY_INFORMATION_PROCESS)GetProcAddress(hModule, "ZwQueryInformationProcess");
            ??? if (ZwQueryInformationProcess == NULL)
            ??? return false;
            ? PROCESS_DEBUG_PORT_INFO ProcessInfo;
            ? if (STATUS_SUCCESS != ZwQueryInformationProcess(GetCurrentProcess( ), ProcessDebugPort, &ProcessInfo, sizeof(ProcessInfo), NULL))
            ??? return false;
            ? else
            ??? if(ProcessInfo.DebugPort)
            ????? return true;
            ??? else
            ????? return false;
            ?????
            2.9 FD_NtQueryInfoProc_DbgObjHandle
            ? 在winxp中引入了"debug object".當(dāng)一個(gè)調(diào)試活動(dòng)開始,一個(gè)"debug object"被創(chuàng)建,同也相應(yīng)產(chǎn)生了一個(gè)句柄。使用為公開的ProcessDebugObjectHandle類,可以查詢這個(gè)句柄的數(shù)值。
            ? 代碼可能還是從pediy里復(fù)制的,不記得了。
            ? HMODULE hModule = GetModuleHandle("ntdll.dll");
            ? ZW_QUERY_INFORMATION_PROCESS ZwQueryInformationProcess;
            ??? ZwQueryInformationProcess = (ZW_QUERY_INFORMATION_PROCESS)GetProcAddress(hModule, "ZwQueryInformationProcess");
            ??? if (ZwQueryInformationProcess == NULL)
            ??? return false;
            ? _PROCESS_DEBUG_OBJECTHANDLE_INFO ProcessInfo;
            ? if (STATUS_SUCCESS != ZwQueryInformationProcess(GetCurrentProcess( ), (PROCESS_INFO_CLASS)0x0000001e, &ProcessInfo, sizeof(ProcessInfo), NULL))
            ??? return false;
            ? else
            ??? if(ProcessInfo.ObjectHandle)
            ????? return true;
            ??? else
            ????? return false;

            2.10 FD_NtQueryInfoProc_DbgFlags();
            同樣的未公開的ProcessDebugFlags類,當(dāng)調(diào)試器存在時(shí),它會(huì)返回false。
            ? HMODULE hModule = GetModuleHandle("ntdll.dll");
            ? ZW_QUERY_INFORMATION_PROCESS ZwQueryInformationProcess;
            ??? ZwQueryInformationProcess = (ZW_QUERY_INFORMATION_PROCESS)GetProcAddress(hModule, "ZwQueryInformationProcess");
            ??? if (ZwQueryInformationProcess == NULL)
            ??? return false;
            ? _PROCESS_DEBUG_FLAGS_INFO ProcessInfo;
            ? if (STATUS_SUCCESS != ZwQueryInformationProcess(GetCurrentProcess( ), (PROCESS_INFO_CLASS)0x0000001f, &ProcessInfo, sizeof(ProcessInfo), NULL))
            ??? return false;
            ? else
            ??? if(ProcessInfo.Debugflags)
            ????? return false;
            ??? else
            ????? return true;

            2.11 FD_NtQueryInfoProc_SysKrlDbgInfo()
            這個(gè)方法估計(jì)對(duì)大家用處不大,SystemKernelDebuggerInformation類同樣可以用來識(shí)別調(diào)試器,只是可惜在windows下無效,據(jù)稱可以用在reactOS中。
            ?? HMODULE hModule = GetModuleHandle("ntdll.dll");
            ??? ZW_QUERY_SYSTEM_INFORMATION ZwQuerySystemInformation;
            ??? ZwQuerySystemInformation = (ZW_QUERY_SYSTEM_INFORMATION)GetProcAddress(hModule, "ZwQuerySystemInformation");
            ??? if (ZwQuerySystemInformation == NULL)
            ??????? return false;
            ??? SYSTEM_KERNEL_DEBUGGER_INFORMATION Info;
            ??? if (STATUS_SUCCESS == ZwQuerySystemInformation(SystemKernelDebuggerInformation, &Info, sizeof(Info), NULL))
            ??? {
            ??????? if (Info.DebuggerEnabled)
            ??????? {
            ??????????? if (Info.DebuggerNotPresent)
            ??????????????? return false;
            ??????????? else
            ??????????????? return true;
            ??????? }
            ??????? else
            ??????????? return false;
            ??? }
            ??? else
            ?????? return true;

            ?

            2.12 FD_SeDebugPrivilege()
            ? 當(dāng)一個(gè)進(jìn)程獲得SeDebugPrivilege,它就獲得了對(duì)CSRSS.EXE的完全控制,這種特權(quán)也會(huì)被子進(jìn)程繼承,也就是說一個(gè)被調(diào)試的程序如果獲得了CSRSS.EXE的進(jìn)程ID,它就可以使用openprocess操作CSRSS.EXE。獲得其進(jìn)程ID有很多中方法,如Process32Next,或NtQuerySystemInformation,在winxp下可以使用CsrGetProcessId。
            ??? hTmp=OpenProcess(PROCESS_ALL_ACCESS,false,PID_csrss);
            ??? if(hTmp!=NULL)
            ??? {
            ????? CloseHandle(hProcessSnap );
            ????? return true;
            ??? }

            2.13 FD_Parent_Process()
            通常我們都直接在windows界面下運(yùn)行應(yīng)用程序,這樣的結(jié)果就是它的父進(jìn)程為"explorer.exe",這個(gè)反調(diào)試就是檢測(cè)應(yīng)用程序的父進(jìn)程是否為"explorer.exe",如不是則判定為處于調(diào)試器中,這也不是百分百可靠,因?yàn)橛械臅r(shí)候你的程序是在命令行提示符下運(yùn)行的。
            Yoda使用了這個(gè)反調(diào)試,它使用Process32Next檢測(cè)父進(jìn)程,目前很多插件已經(jīng)通過使Process32Next始終返回false來越過這個(gè)反調(diào)試(比如HideOD)。不過可以對(duì)代碼做些簡(jiǎn)單的修正來處理這個(gè)反反調(diào)試。

            2.14 FD_DebugObject_NtQueryObject();
            ? 如前面所描述的,當(dāng)一個(gè)調(diào)試活動(dòng)開始,一個(gè)"debug object"被創(chuàng)建,同也相應(yīng)產(chǎn)生了一個(gè)句柄。我們可以查詢這個(gè)調(diào)試對(duì)象列表,并檢查調(diào)試對(duì)象的數(shù)量,以實(shí)現(xiàn)調(diào)試器的檢測(cè)。
            ? HMODULE hModule = GetModuleHandle("ntdll.dll");
            ? PNtQueryObject NtQueryObject;
            ? NtQueryObject = (PNtQueryObject)GetProcAddress(hModule,"NtQueryObject");

            ? if(NtQueryObject==NULL)
            ??? return false;
            ? unsigned char szdbgobj[25]=
            ? "\x44\x00\x65\x00\x62\x00\x75\x00\x67\x00\x4f\x00\x62\x00\x6a\x00\x65\x00\x63\x00\x74\x00\x00\x00";
            ? unsigned char *psz=&szdbgobj[0];
            ? __asm
            ? {
            ??? xor??? ebx,ebx;
            ??? push? ebx;
            ??? push? esp;
            ??? push? ebx;
            ??? push? ebx;
            ??? push? 3;
            ??? push? ebx;
            ??? Call? dword ptr [NtQueryObject];
            ??? pop? edi;
            ??? push? 4;
            ??? push? 1000h;
            ??? push? edi;
            ??? push? ebx;
            ????? call? dword ptr [VirtualAlloc];
            ??? push? ebx;
            ??? push? edi;
            ??? push? eax;
            ??? push? 3;
            ??? push? ebx;
            ??? xchg? esi,eax;
            ??? Call? dword ptr [NtQueryObject];
            ??? lodsd;
            ??? xchg? ecx,eax;
            lable1:? lodsd;
            ??? movzx? edx,ax;
            ??? lodsd;
            ??? xchg? esi,eax;
            ??? cmp??? edx,16h;
            ??? jne??? label2;
            ??? xchg? ecx,edx;
            ??? mov??? edi,psz;
            ??? repe? cmp**;
            ??? xchg? ecx,edx;
            ??? jne??? label2;
            ??? cmp??? dword ptr [eax],edx
            ??? jne??? rt_label;
            lable2:? add??? esi,edx
            ??? and??? esi,-4;
            ??? lodsd
            ??? loop? label1;
            ? }
            ? return false;
            rt_label:
            ? return true;
            2.15 FD_Find_Debugger_Window();
            通過列舉運(yùn)行的應(yīng)用程序的窗口,并于常用調(diào)試相關(guān)工具比對(duì)的方法,應(yīng)該很常用了,就不多說了。這個(gè)也是個(gè)可以自行增加項(xiàng)目的函數(shù),你可以將一些常用的調(diào)試工具歸入其中,比如OD,IDA,WindBG,SoftICE等,你也可以添加任何你需要的,比如"Import REConstructor v1.6 FINAL (C) 2001-2003 MackT/uCF","Registry Monitor - Sysinternals: ? //ollyice
            ??? hWnd=CWnd::FindWindow(_T("1212121"),NULL);
            ??? if (hWnd!=NULL)
            ??? return true;
            ? //ollydbg v1.1
            ??? hWnd=CWnd::FindWindow(_T("icu_dbg"),NULL);
            ??? if (hWnd!=NULL)
            ??? return true;
            ? //ollyice pe--diy
            ??? hWnd=CWnd::FindWindow(_T("pe--diy"),NULL);
            ??? if (hWnd!=NULL)
            ??? return true;
            ? //ollydbg ?-°?
            ??? hWnd=CWnd::FindWindow(_T("ollydbg"),NULL);
            ??? if (hWnd!=NULL)
            ??? return true;
            ? //ollydbg ?-°?
            ??? hWnd=CWnd::FindWindow(_T("odbydyk"),NULL);
            ??? if (hWnd!=NULL)
            ??? return true;
            ? //windbg
            ??? hWnd=CWnd::FindWindow(_T("WinDbgFrameClass"),NULL);
            ??? if (hWnd!=NULL)
            ??? return true;
            ? //dede3.50
            ??? hWnd=CWnd::FindWindow(_T("TDeDeMainForm"),NULL);
            ??? if (hWnd!=NULL)
            ??? return true;
            ? //IDA5.20
            ??? hWnd=CWnd::FindWindow(_T("TIdaWindow"),NULL);
            ??? if (hWnd!=NULL)
            ??? return true;
            ? //others
            ??? hWnd=CWnd::FindWindow(_T("TESTDBG"),NULL);
            ??? if (hWnd!=NULL)
            ??? return true;
            ??? hWnd=CWnd::FindWindow(_T("kk1"),NULL);
            ??? if (hWnd!=NULL)
            ??? return true;
            ??? hWnd=CWnd::FindWindow(_T("Eew75"),NULL);
            ??? if (hWnd!=NULL)
            ??? return true;
            ??? hWnd=CWnd::FindWindow(_T("Shadow"),NULL);
            ??? if (hWnd!=NULL)
            ??? return true;
            ? //PEiD v0.94
            ??? hWnd=CWnd::FindWindow(NULL,"PEiD v0.94");
            ??? if (hWnd!=NULL)
            ??? return true;
            ? //RegMON
            ??? hWnd=CWnd::FindWindow(NULL,"Registry Monitor - Sysinternals:
            ??? if (hWnd!=NULL)
            ??? return true;
            ? //File Monitor
            ??? hWnd=CWnd::FindWindow(NULL,"File Monitor - Sysinternals:
            ??? if (hWnd!=NULL)
            ??? return true;
            ? //Import Rec v1.6
            ??? hWnd=CWnd::FindWindow(NULL,"Import REConstructor v1.6 FINAL (C) 2001-2003 MackT/uCF");
            ??? if (hWnd!=NULL)
            ??? return true;
            ? return false;
            ?
            2.16 FD_Find_Debugger_Process();
            ? 與上面的方法類似,區(qū)別是這個(gè)反調(diào)試用通過查詢進(jìn)程名字與已知的常用調(diào)試器應(yīng)用程序名字進(jìn)行比對(duì),以確定是否有調(diào)試器處于運(yùn)行狀態(tài)。
            ??? if(strcmp(pe32.szExeFile,"OLLYICE.EXE")==0)
            ??????? return true;
            ??? if(strcmp(pe32.szExeFile,"IDAG.EXE")==0)
            ??????? return true;
            ??? if(strcmp(pe32.szExeFile,"OLLYDBG.EXE")==0)
            ??????? return true;
            ??? if(strcmp(pe32.szExeFile,"PEID.EXE")==0)
            ??????? return true;
            ??? if(strcmp(pe32.szExeFile,"SOFTICE.EXE")==0)
            ??????? return true;
            ??? if(strcmp(pe32.szExeFile,"LORDPE.EXE")==0)
            ??????? return true;
            ??? if(strcmp(pe32.szExeFile,"IMPORTREC.EXE")==0)
            ??????? return true;
            ??? if(strcmp(pe32.szExeFile,"W32DSM89.EXE")==0)
            ??????? return true;
            ??? if(strcmp(pe32.szExeFile,"WINDBG.EXE")==0)
            ??????? return true;


            2.17 FD_Find_Device_Driver()
            ? 調(diào)試工具通常會(huì)使用內(nèi)核驅(qū)動(dòng),因此如果嘗試是否可以打開一些調(diào)試器所用到的設(shè)備,就可判斷是否存在調(diào)試器。常用的設(shè)備名稱如下:
            \\.\SICE? (SoftICE)
            \\.\SIWVID(SoftICE)???
            \\.\NTICE? (SoftICE)???
            \\.\REGVXG(RegMON)
            \\.\REGVXD(RegMON)
            \\.\REGSYS(RegMON)
            \\.\REGSYS(RegMON)
            \\.\FILEVXG(FileMON)
            \\.\FILEM(FileMON)
            \\.\TRW(TRW2000)

            2.18 FD_Exception_Closehandle()
            ? 如果給CloseHandle()函數(shù)一個(gè)無效句柄作為輸入?yún)?shù),在無調(diào)試器時(shí),將會(huì)返回一個(gè)錯(cuò)誤代碼,而有調(diào)試器存在時(shí),將會(huì)觸發(fā)一個(gè)EXCEPTION_INVALID_HANDLE (0xc0000008)的異常。
            ? __try?
            ? {
            ??? CloseHandle(HANDLE(0x00001234));
            ??? return false;
            ? }
            ? __except(1)
            ? {
            ??? return true;
            ? }

            ?

            2.19 FD_Exception_Int3()
            ? 通過Int3產(chǎn)生異常中斷的反調(diào)試比較經(jīng)典。當(dāng)INT3 被執(zhí)行到時(shí), 如果程序未被調(diào)試, 將會(huì)異常處理器程序繼續(xù)執(zhí)行。而INT3指令常被調(diào)試器用于設(shè)置軟件斷點(diǎn),int 3會(huì)導(dǎo)致調(diào)試器誤認(rèn)為這是一個(gè)自己的斷點(diǎn),從而不會(huì)進(jìn)入異常處理程序。
            ? __asm
            ? {
            ??? push?? offset exception_handler; set exception handler
            ??? push? dword ptr fs:[0h]
            ??? mov??? dword ptr fs:[0h],esp?
            ??? xor?? eax,eax;reset EAX invoke int3
            ??? int??? 3h
            ??? pop??? dword ptr fs:[0h];restore exception handler
            ??? add?? esp,4

            ??? test?? eax,eax; check the flag
            ??? je??? rt_label
            ??? jmp??? rf_label

            exception_handler:
            ??? mov?? eax,dword ptr [esp+0xc];EAX = ContextRecord
            ??? mov??? dword ptr [eax+0xb0],0xffffffff;set flag (ContextRecord.EAX)
            ??? inc?? dword ptr [eax+0xb8];set ContextRecord.EIP
            ??? xor?? eax,eax
            ??? retn

            rt_label:
            ??? xor eax,eax
            ??? inc eax
            ??? mov esp,ebp
            ??? pop ebp
            ??? retn
            rf_label:
            ??? xor eax,eax
            ??? mov esp,ebp
            ??? pop ebp
            ??? retn
            ? }

            2.20 FD_Exception_Popf()
            我們都知道標(biāo)志寄存器中的陷阱標(biāo)志,當(dāng)該標(biāo)志被設(shè)置時(shí),將產(chǎn)生一個(gè)單步異常。在程序中動(dòng)態(tài)設(shè)置這給標(biāo)志,如果處于調(diào)試器中,該異常將會(huì)被調(diào)試器捕獲。
            可通過下面的代碼設(shè)置標(biāo)志寄存器。
            ??? pushf
            ??? mov dword ptr [esp], 0x100
            ??? popf

            2.21 FD_OutputDebugString()
            ? 在有調(diào)試器存在和沒有調(diào)試器存在時(shí),OutputDebugString函數(shù)表現(xiàn)會(huì)有所不同。最明顯的不同是, 如果有調(diào)試器存在,其后的GetLastError()的返回值為零。
            ? OutputDebugString("");
            ? tmpD=GetLastError();
            ? if(tmpD==0)
            ??? return true;
            ? return false;

            2.22 FD_TEB_check_in_Vista();
            ? 這是從windows anti-debug reference里拷貝出來的,據(jù)說是適用于vista系統(tǒng)下檢測(cè)調(diào)試器。我沒有vista所以也沒有測(cè)試。有條件的可以試下,有問題幫忙反饋給我。多謝。
            ??? //vista
            ??? __asm
            ??? {
            ????? push?? offset exception_handler; set exception handler
            ????? push? dword ptr fs:[0h]
            ????? mov??? dword ptr fs:[0h],esp?
            ????? xor?? eax,eax;reset EAX invoke int3
            ????? int??? 3h
            ????? pop??? dword ptr fs:[0h];restore exception handler
            ????? add?? esp,4
            ????? mov eax, fs:[18h] ; teb
            ????? add eax, 0BFCh
            ????? mov ebx, [eax] ; pointer to a unicode string
            ????? test ebx, ebx ; (ntdll.dll, gdi32.dll,...)
            ????? je????? rf_label
            ????? jmp??? rt_label
            ? exception_handler:
            ????? mov?? eax,dword ptr [esp+0xc];EAX = ContextRecord
            ????? inc?? dword ptr [eax+0xb8];set ContextRecord.EIP
            ????? xor?? eax,eax
            ????? retn
            ??? }

            2.23 FD_check_StartupInfo();
            ? 這是從pediy上拷貝來的。Window創(chuàng)建進(jìn)程的時(shí)候會(huì)把STARTUPINFO結(jié)構(gòu)中的值設(shè)為0,而通過調(diào)試器創(chuàng)建進(jìn)程的時(shí)候會(huì)忽略這個(gè)結(jié)構(gòu)中的值,也就是結(jié)構(gòu)中的值不為0,所以可以利用這個(gè)來判斷是否在調(diào)試程序。
            ? STARTUPINFO si;
            ? ZeroMemory( &si, sizeof(si) );
            ? si.cb = sizeof(si);
            ? GetStartupInfo(&si);
            ? if ( (si.dwX != 0) || (si.dwY !=0)
            ??? || (si.dwXCountChars != 0) || (si.dwYCountChars !=0 )
            ??? || (si.dwFillAttribute != 0) || (si.dwXSize != 0)
            ??? || (si.dwYSize != 0) )
            ??? return true;
            ? else?
            ??? return false;
            ???
            2.24 FD_Parent_Process1()
            與前面的FD_Parent_Process原理一樣,唯一不同的是使用ZwQueryInformationProcess檢測(cè)父進(jìn)程,而沒有使用Process32Next,這有一個(gè)好處是可以繞過OD的HideOD插件。

            2.25 FD_Exception_Instruction_count()
            ? 好像《軟件加解密技術(shù)》中有提到這個(gè)反調(diào)試。
            ? 通過注冊(cè)一個(gè)異常句柄,在特定地址設(shè)置一些硬件斷點(diǎn),當(dāng)通過這些地址時(shí)都會(huì)觸發(fā)EXCEPTION_SINGLE_STEP (0x80000004)的異常,在異常處理程序中,將會(huì)調(diào)整指令指針到一條新指令,然后恢復(fù)運(yùn)行。可以通過進(jìn)入進(jìn)程context結(jié)構(gòu)來設(shè)置這些斷點(diǎn),有些調(diào)試器不能處理那些不是自己設(shè)置的硬件斷點(diǎn),從而導(dǎo)致一些指令將會(huì)被漏掉計(jì)數(shù),這就形成了一個(gè)反調(diào)試。
            ? __asm
            ? {
            ??? xor??? eax,eax;
            ??? cdq;
            ??? push? e_handler;
            ??? push? dword ptr fs:[eax];
            ??? mov??? fs:[eax],esp;
            ??? int 3;
            hwbp1:? nop
            hwbp2:? nop
            hwbp3:? nop
            hwbp4:? nop
            ??? div??? edx
            ??? nop
            ??? pop??? dword ptr fs:[0]
            ??? add??? esp,4
            ??? cmp??? al,4;
            ??? jne??? rt_label;
            ??? jmp??? rf_label;

            e_handler:
            ??? xor??? eax,eax;
            ??? ;ExceptionRecord
            ??? mov??? ecx,dword ptr[esp+0x04]
            ??? ;Contextrecord
            ??? mov??? edx,dword ptr[esp+0x0c]
            ??? ;ContextEIP
            ??? inc??? byte ptr[edx+0xb8];
            ???
            ??? ;ExceptionCode
            ??? mov??? ecx,dword ptr[ecx];

            ??? ;1.EXCEPTION_INT_DIVIDE_BY_ZERO
            ??? cmp??? ecx,0xc0000094;
            ??? jne??? Ex_next2;
            ??? ;Context_eip
            ??? inc??? byte ptr[edx+0xb8];
            ??? mov??? dword ptr[edx+0x04],eax;dr0
            ??? mov??? dword ptr[edx+0x08],eax;dr1
            ??? mov??? dword ptr[edx+0x0c],eax;dr2
            ??? mov??? dword ptr[edx+0x10],eax;dr3
            ??? mov??? dword ptr[edx+0x14],eax;dr6
            ??? mov??? dword ptr[edx+0x18],eax;dr7
            ??? ret

            ??? ;2.EXCEPTION_BREAKPOINT
            Ex_next2:
            ??? cmp??? ecx,0x80000003;
            ??? jne??? Ex_next3;

            ??? mov??? dword ptr[edx+0x04],offset hwbp1;dr0
            ??? mov??? dword ptr[edx+0x08],offset hwbp2;dr1
            ??? mov??? dword ptr[edx+0x0c],offset hwbp3;dr2
            ??? mov??? dword ptr[edx+0x10],offset hwbp4;dr3
            ??? mov??? dword ptr[edx+0x18],0x155;dr7
            ??? ret

            ??? ;3.EXCEPTION_SINGLE_STEP
            Ex_next3:
            ??? cmp? ecx,0x80000004
            ??? jne??? rt_label
            ??? ;CONTEXT_Eax
            ??? inc??? byte ptr[edx+0xb0]
            ??? ret
            ? }

            2.26 FD_INT_2d()
            在windows anti-debug reference中指出,如果程序未被調(diào)試這個(gè)中斷將會(huì)生產(chǎn)一個(gè)斷點(diǎn)異常. 被調(diào)試并且未使用跟蹤標(biāo)志執(zhí)行這個(gè)指令, 將不會(huì)有異常產(chǎn)生程序正常執(zhí)行. 如果被調(diào)試并且指令被跟蹤, 尾隨的字節(jié)將被跳過并且執(zhí)行繼續(xù). 因此, 使用 INT 2Dh 能作為一個(gè)強(qiáng)有力的反調(diào)試和反跟蹤機(jī)制。
            ? __try
            ? {
            ??? __asm
            ??? {
            ??????? int 2dh
            ????? inc eax;any opcode of singlebyte.
            ????? ;or u can put some junkcode,"0xc8"..."0xc2"..."0xe8"..."0xe9"
            ??? }
            ? return true;
            ? }
            ? __except(1)
            ? {
            ??? return false;
            ? }

            ?

            三、? 檢測(cè)-專用調(diào)試器(FS_)
            ??? 這一部分是我比較喜歡的,但內(nèi)容還不是很豐富,比如:
            1、? 針對(duì)SoftIce的檢測(cè)方法有很多,但由于我從沒使用過Softice,也沒有條件去測(cè)試,所以沒有給出太多,有興趣的可以自己查閱資料進(jìn)行補(bǔ)充,針對(duì)softice網(wǎng)上資料較多,或查閱《軟件加解密技術(shù)》。
            2、? 同樣,這里也沒有給出windbg等等其它調(diào)試器的檢測(cè)方法。
            3、? 而針對(duì)Odplugin,也只給了幾種HideOD的檢測(cè)。事實(shí)上,目前OD的使用者通常都使用眾多的強(qiáng)大插件,當(dāng)OD的反調(diào)試越來越普遍時(shí),自己設(shè)計(jì)幾款常用的OD插件的反調(diào)試,將會(huì)是非常有效的反調(diào)試手段。
            4、? 對(duì)VME的檢測(cè)也只給出了兩種,如想豐富這一部分可以參考Peter Ferrie的一篇anti-vme的文章(http://bbs.pediy.com/showthread.php?t=68411)。里面有非常多的anti-vme方法。

            ??? 針對(duì)專用調(diào)試器的函數(shù)列表如下:
            //find specific debugger
            bool FS_OD_Exception_GuardPages();
            bool FS_OD_Int3_Pushfd();
            bool FS_SI_UnhandledExceptionFilter();
            bool FS_ODP_Process32NextW();
            bool FS_ODP_OutputDebugStringA();
            bool FS_ODP_OpenProcess();
            bool FS_ODP_CheckRemoteDebuggerPresent();
            bool FS_ODP_ZwSetInformationThread();
            bool FS_SI_Exception_Int1();
            bool IsInsideVMWare_();
            bool FV_VMWare_VMX();
            bool FV_VPC_Exception();
            int FV_VME_RedPill();//0:none,1:vmvare;2:vpc;3:others

            3.1 FS_OD_Exception_GuardPages
            ??? “保護(hù)頁異常”是一個(gè)簡(jiǎn)單的反調(diào)試技巧。當(dāng)應(yīng)用程序嘗試執(zhí)行保護(hù)頁內(nèi)的代碼時(shí),將會(huì)產(chǎn)生一個(gè)EXCEPTION_GUARD_PAGE(0x80000001)異常,但如果存在調(diào)試器,調(diào)試器有可能接收這個(gè)異常,并允許該程序繼續(xù)運(yùn)行,事實(shí)上,在OD中就是這樣處理的,OD使用保護(hù)頁來實(shí)現(xiàn)內(nèi)存斷點(diǎn)。
            最開始實(shí)現(xiàn)時(shí)忘記了free申請(qǐng)的空間,多謝sessiondiy提醒。

            ? SYSTEM_INFO sSysInfo;
            ? LPVOID lpvBase;
            ? BYTE * lptmpB;
            ? GetSystemInfo(&sSysInfo);
            ? DWORD dwPageSize=sSysInfo.dwPageSize;
            ? DWORD flOldProtect;

            ? DWORD dwErrorcode;

            ? lpvBase=VirtualAlloc(NULL,dwPageSize,MEM_COMMIT,PAGE_READWRITE);
            ? if(lpvBase==NULL)
            ??? return false;
            ?
            ? lptmpB=(BYTE *)lpvBase;
            ? *lptmpB=0xc3;//retn

            ? VirtualProtect(lpvBase,dwPageSize,PAGE_EXECUTE_READ | PAGE_GUARD,&flOldProtect);
            ?
            ? __try
            ? {
            ??? __asm? call dword ptr[lpvBase];
            ??? VirtualFree(lpvBase,0,MEM_RELEASE);
            ??? return true;
            ? }
            ? __except(1)
            ? {
            ??? VirtualFree(lpvBase,0,MEM_RELEASE);
            ??? return false;
            ? }

            3.2 FS_OD_Int3_Pushfd
            ??? 這是個(gè)最近比較牛X的反調(diào)試,據(jù)稱是vmp1.64里發(fā)現(xiàn)的,好像ttprotect里面也有使用,我沒有驗(yàn)證。Pediy里有帖子詳細(xì)討論,我是看到gkend的分析,才搞懂一些。下面摘自gkend分析

            代碼:
            ??? int3,pushfd和int3,popfd一樣的效果。只要修改int3后面的popfd為其他值,OD都能通過。老掉牙的技術(shù)又重新被用了。SEH異常機(jī)制的運(yùn)用而已。
            ??? 原理:在SEH異常處理中設(shè)置了硬件斷點(diǎn)DR0=EIP+2,并把EIP的值加2,那么應(yīng)該在int3,popfd后面的指令執(zhí)行時(shí)會(huì)產(chǎn)生單步異常。但是OD遇到前面是popfd/pushfd時(shí),OD會(huì)自動(dòng)在popfd后一指令處設(shè)置硬件斷點(diǎn),而VMP的seh異常處理會(huì)判斷是否已經(jīng)設(shè)置硬件斷點(diǎn),如果已經(jīng)有硬件斷點(diǎn)就不產(chǎn)生單步異常,所以不能正常執(zhí)行。
            ??? http://bbs.pediy.com/showthread.php?t=67737
            ??? 大家也可以仔細(xì)研究下OD下的pushfd,popfd等指令,相信利用它們可以構(gòu)造很多反調(diào)試,下面是我實(shí)現(xiàn)的一個(gè),不過現(xiàn)在看起來有點(diǎn)沒看懂,不知當(dāng)時(shí)為什么用了兩個(gè)int3。
            ? __asm
            ? {
            ??? push?? offset e_handler; set exception handler
            ??? push? dword ptr fs:[0h]
            ??? mov??? dword ptr fs:[0h],esp?
            ??? xor?? eax,eax;reset EAX invoke int3
            ??? int??? 3h
            ??? pushfd
            ??? nop
            ??? nop
            ??? nop
            ??? nop
            ??? pop??? dword ptr fs:[0h];restore exception handler
            ??? add?? esp,4

            ??? test?? eax,eax; check the flag
            ??? je??? rf_label
            ??? jmp??? rt_label

            e_handler:
            ??? push?? offset e_handler1; set exception handler
            ??? push? dword ptr fs:[0h]
            ??? mov??? dword ptr fs:[0h],esp?
            ??? xor?? eax,eax;reset EAX invoke int3
            ??? int??? 3h
            ??? nop
            ??? pop??? dword ptr fs:[0h];restore exception handler
            ??? add?? esp,4
            ??? ;EAX = ContextRecord
            ??? mov??? ebx,eax;dr0=>ebx
            ??? mov?? eax,dword ptr [esp+0xc]
            ??? ;set ContextRecord.EIP
            ??? inc?? dword ptr [eax+0xb8];
            ??? mov??? dword ptr [eax+0xb0],ebx;dr0=>eax
            ??? xor??? eax,eax
            ??? retn

            e_handler1:
            ??? ;EAX = ContextRecord
            ??? mov?? eax,dword ptr [esp+0xc]
            ??? ;set ContextRecord.EIP
            ??? inc?? dword ptr [eax+0xb8];
            ??? mov??? ebx,dword ptr[eax+0x04]
            ??? mov??? dword ptr [eax+0xb0],ebx;dr0=>eax
            ??? xor??? eax,eax
            ??? retn
            rt_label:
            ??? xor? eax,eax
            ??? inc eax
            ??? mov esp,ebp
            ??? pop? ebp
            ??? retn
            rf_label:
            ??? xor eax,eax
            ??? mov esp,ebp
            ??? pop ebp
            ??? retn
            ? }

            3.3 FS_SI_UnhandledExceptionFilter
            ??? 這個(gè)針對(duì)SoftIce的反調(diào)試很簡(jiǎn)單,好像是SoftIce會(huì)修改UnhandledExceptionFilter這個(gè)函數(shù)的第一個(gè)字節(jié)為CC。因此判斷這個(gè)字節(jié)是否為cc,就是一種檢查softice的簡(jiǎn)便方法。

            FARPROC Uaddr ;
            BYTE tmpB = 0;
            (FARPROC&) Uaddr =GetProcAddress ( GetModuleHandle("kernel32.dll"),"UnhandledExceptionFilter");
            tmpB = *((BYTE*)Uaddr);?? // 取UnhandledExceptionFilter函數(shù)第一字節(jié)
            tmpB=tmpB^0x55;
            if(tmpB ==0x99)?????????? // 如該字節(jié)為CC,則SoftICE己加載
            ? return true;
            else?
            ? return false;

            3.4 FS_ODP_Process32NextW
            ??? 當(dāng)我在調(diào)試FD_parentprocess時(shí),感覺總是怪怪的,使用OD時(shí)運(yùn)行Process32NextW總是返回失敗,搞了一個(gè)晚上,才搞懂原來是OD的插件HideOD在作怪。當(dāng)HideOD的Process32NextW的選項(xiàng)被選中時(shí),它會(huì)更改Process32NextW的返回值,使其始終返回false,這主要是HideOD針對(duì)FD_parentprocess這個(gè)反調(diào)試的一個(gè)反反調(diào)試。但也正是這一點(diǎn)暴露的它的存在。
            ? int OSVersion;
            ? FARPROC Func_addr;
            ? WORD tmpW;
            ? //1.Process32Next
            ? HMODULE hModule = GetModuleHandle("kernel32.dll");
            ? (FARPROC&) Func_addr =GetProcAddress ( hModule,"Process32NextW");
            ? if (Func_addr != NULL)
            ? {
            ??? tmpW=*(WORD*)Func_addr;
            ??? OSVersion=myGetOSVersion();
            ??? switch(OSVersion)
            ??? {
            ??? case OS_winxp:
            ????? if(tmpW!=0xFF8B)//maybe option of Process32Next is selected.
            ??????? return true;
            ????? break;
            ??? default:
            ????? if(tmpW==0xC033)
            ??????? return true;
            ????? break;
            ??? }
            ? }
            ??? 但上面的代碼并不完美,因?yàn)橛锌缙脚_(tái)問題,所以要先獲得當(dāng)前操作系統(tǒng)版本。目前只在win2k和winxp下進(jìn)行了測(cè)試。

            3.5 FS_ODP_OutputDebugStringA
            ??? 同樣,HIDEOD的OutputDebugStringA選項(xiàng),也對(duì)OutputDebugStringA這個(gè)api做了處理,具體修改內(nèi)容我記不得了,大家可以自己比對(duì)一下。我的代碼如下:
            ? int OSVersion;
            ? FARPROC Func_addr;
            ? WORD tmpW;
            ? //2.OutputDebugStringA
            ? HMODULE hModule = GetModuleHandle("kernel32.dll");
            ? (FARPROC&) Func_addr =GetProcAddress ( hModule,"OutputDebugStringA");
            ? if (Func_addr != NULL)
            ? {
            ??? tmpW=*(WORD*)Func_addr;
            ??? OSVersion=myGetOSVersion();
            ??? switch(OSVersion)
            ??? {
            ??? case OS_winxp:
            ????? if(tmpW!=0x3468)//maybe option of OutputDebugStringAt is selected.
            ??????? return true;
            ????? break;
            ??? default:
            ????? if(tmpW==0x01e8)
            ??????? return true;
            ????? break;
            ??? }
            ? }
            ? return false;

            3.6 FS_ODP_OpenProcess
            ??? 這個(gè)據(jù)稱這個(gè)是針對(duì)HideDebugger這個(gè)插件的,當(dāng)這個(gè)插件開啟時(shí),它會(huì)掛鉤OpenProcess這個(gè)函數(shù),它修改了OpenProcess的前幾個(gè)字節(jié)。因此檢測(cè)這幾個(gè)字節(jié)就可實(shí)現(xiàn)這個(gè)反調(diào)試。
            ? FARPROC Func_addr;
            ? BYTE tmpB;
            ? //OpenProcess
            ? HMODULE hModule = GetModuleHandle("kernel32.dll");
            ? (FARPROC&) Func_addr =GetProcAddress ( hModule,"OpenProcess");
            ? if (Func_addr != NULL)
            ? {
            ??? tmpB=*((BYTE*)Func_addr+6);
            ??? if(tmpB==0xea)//HideDebugger Plugin of OD is present
            ??????? return true;
            ? }
            ? return false;

            3.7 FS_ODP_CheckRemoteDebuggerPresent
            ??? 和前面提到的兩個(gè)HideOD的反調(diào)試類似,不多說了。大家可以自行比對(duì)一下開啟和不開啟HideOD時(shí),CheckRemoteDebuggerPresent函數(shù)的異同,就可以設(shè)計(jì)反這個(gè)插件的反調(diào)試了。
            ? int OSVersion;
            ? FARPROC Func_addr;
            ? BYTE tmpB;
            ? //2.CheckRemoteDebuggerPresent
            ? HMODULE hModule = GetModuleHandle("kernel32.dll");
            ? (FARPROC&) Func_addr =GetProcAddress ( hModule,"CheckRemoteDebuggerPresent");
            ? if (Func_addr != NULL)
            ? {
            ??? tmpB=*((BYTE*)Func_addr+10);
            ??? OSVersion=myGetOSVersion();
            ??? switch(OSVersion)
            ??? {
            ??? case OS_winxp:
            ????? if(tmpB!=0x74)//HideOD is present
            ??????? return true;
            ????? break;
            ??? default:
            ????? break;
            ??? }
            ? }
            ? return false;

            3.8 FS_ODP_ZwSetInformationThread
            ??? 和前面提到的幾個(gè)HideOD的反調(diào)試類似,大家可以自行比對(duì)一下開啟和不開啟HideOD時(shí),ZwSetInformationThread函數(shù)的異同,就可以設(shè)計(jì)反這個(gè)插件的反調(diào)試了。
            ? int OSVersion;
            ? FARPROC Func_addr;
            ? WORD tmpW;
            ? BYTE tmpB0,tmpB1;
            ? //2.CheckRemoteDebuggerPresent
            ? HMODULE hModule = GetModuleHandle("ntdll.dll");
            ? (FARPROC&) Func_addr =GetProcAddress ( hModule,"ZwSetInformationThread");
            ? if (Func_addr != NULL)
            ? {
            ??? tmpW=*((WORD*)Func_addr+3);
            ??? tmpB0=*((BYTE*)Func_addr+9);
            ??? tmpB1=*((BYTE*)Func_addr+10);
            ??? OSVersion=myGetOSVersion();
            ??? switch(OSVersion)
            ??? {
            ??? case OS_winxp:
            ????? if(tmpW!=0x0300)//HideOD is present
            ??????? return true;
            ????? break;
            ??? case OS_win2k:
            ????? if((tmpB0!=0xcd)&&(tmpB1!=0x2e))
            ??????? return true;
            ????? break;
            ??? default:
            ????? break;
            ??? }
            ? }
            ? return false;

            3.9 FS_SI_Exception_Int1
            ??? 通常int1的DPL為0,這表示"cd 01"機(jī)器碼不能在3環(huán)下執(zhí)行。如果直接執(zhí)行這個(gè)中斷將會(huì)產(chǎn)生一個(gè)保護(hù)錯(cuò)誤,windows會(huì)產(chǎn)生一個(gè)EXCEPTION_ACCESS_VIOLATION (0xc0000005)異常。然而,如果SOFTICE正在運(yùn)行,它掛鉤了int1,并調(diào)整其DPL為3。這樣SoftICE就可以在用戶模式執(zhí)行單步操作了。
            ??? 當(dāng)int 1發(fā)生時(shí),SoftICE不檢查它是由于陷阱標(biāo)志位還是由軟件中斷產(chǎn)生,SoftICE總是去調(diào)用原始中斷1的句柄,此時(shí)將會(huì)產(chǎn)生一個(gè)EXCEPTION_SINGLE_STEP (0x80000004)而不是EXCEPTION_ACCESS_VIOLATION (0xc0000005)異常,這就形成了一個(gè)簡(jiǎn)單的反調(diào)試方法。

            ? __asm
            ? {
            ??? push?? offset eh_int1; set exception handler
            ??? push? dword ptr fs:[0h]
            ??? mov??? dword ptr fs:[0h],esp?
            ??? xor?? eax,eax;reset flag(EAX) invoke int3
            ??? int??? 1h
            ??? pop??? dword ptr fs:[0h];restore exception handler
            ??? add?? esp,4

            ??? cmp??? eax,0x80000004; check the flag
            ??? je??? rt_label_int1
            ??? jmp??? rf_label_int1

            eh_int1:
            ??? mov??? eax,[esp+0x4];
            ??? mov??? ebx,dword ptr [eax];
            ??? mov?? eax,dword ptr [esp+0xc];EAX = ContextRecord
            ??? mov??? dword ptr [eax+0xb0],ebx;set flag (ContextRecord.EAX)

            ??? inc?? dword ptr [eax+0xb8];set ContextRecord.EIP
            ??? inc?? dword ptr [eax+0xb8];set ContextRecord.EIP
            ??? xor?? eax,eax
            ??? retn
            ? }

            3.10 FV_VMWare_VMX
            ??? 這是一個(gè)針對(duì)VMWare虛擬機(jī)仿真環(huán)境的反調(diào)試,我從網(wǎng)上直接拷貝的代碼。
            ??? VMWARE提供一種主機(jī)和客戶機(jī)之間的通信方法,這可以被用做一種VMWare的反調(diào)試。Vmware將會(huì)處理IN (端口為0x5658/’VX’)指令,它會(huì)返回一個(gè)majic數(shù)值“VMXh”到EBX中。
            ??? 當(dāng)在保護(hù)模式操作系統(tǒng)的3環(huán)下運(yùn)行時(shí),IN指令的執(zhí)行將會(huì)產(chǎn)生一個(gè)異常,除非我們修改了I/O的優(yōu)先級(jí)等級(jí)。然而,如果在VMWare下運(yùn)行,將不會(huì)產(chǎn)生任何異常,同時(shí)EBX寄存器將會(huì)包含’VMXh’,ECX寄存器也會(huì)被修改為Vmware的產(chǎn)品ID。
            ??? 這種技巧在一些病毒中比較常用。
            ??? 針對(duì)VME的反調(diào)試,在peter Ferrie的另一篇文章<<Attacks on More Virtual Machine Emulators>>中有大量的描述,有興趣的可以根據(jù)這篇文章,將FV_反調(diào)試好好豐富一下。

            bool IsInsideVMWare_()
            {
            ? bool r;
            ? _asm
            ? {
            ??? push?? edx
            ??? push?? ecx
            ??? push?? ebx

            ??? mov??? eax, 'VMXh'
            ??? mov??? ebx, 0 // any value but MAGIC VALUE
            ??? mov??? ecx, 10 // get VMWare version
            ??? mov??? edx, 'VX' // port number
            ??? in???? eax, dx // read port
            ?????????????????? // on return EAX returns the VERSION
            ??? cmp??? ebx, 'VMXh' // is it a reply from VMWare?
            ??? setz?? [r] // set return value

            ??? pop??? ebx
            ??? pop??? ecx
            ??? pop??? edx
            ? }
            ? return r;
            }

            bool FV_VMWare_VMX()
            {
            ? __try
            ? {
            ??? return IsInsideVMWare_();
            ? }
            ? __except(1) // 1 = EXCEPTION_EXECUTE_HANDLER
            ? {
            ??? return false;
            ? }
            }

            3.11 FV_VPC_Exception
            ??? 這個(gè)代碼我也是完整從網(wǎng)上拷貝下來的,具體原理在<<Attacks on More Virtual Machine Emulators>>這篇文章里也有詳細(xì)描述。與VMWare使用一個(gè)特殊端口完成主機(jī)和客戶機(jī)間通信的方法類似的是,VirtualPC靠執(zhí)行非法指令產(chǎn)生一個(gè)異常供內(nèi)核捕獲。這個(gè)代碼如下:

            代碼:
            0F 3F x1 x2
            0F C7 C8 y1 y2
            ??? 由這兩個(gè)非法指令引起的異常將會(huì)被應(yīng)用程序捕獲,然而,如果VirtualPC正在運(yùn)行,將不會(huì)產(chǎn)生異常。X1,x2的允許的數(shù)值還不知道,但有一部分已知可以使用,如0A 00,11 00…等等。
            __declspec(naked) bool FV_VPC_Exception()
            {
            ? _asm
            ? {
            ??? push ebp
            ??? mov? ebp, esp

            ??? mov? ecx, offset exception_handler

            ??? push ebx
            ??? push ecx

            ??? push dword ptr fs:[0]
            ??? mov? dword ptr fs:[0], esp

            ??? mov? ebx, 0 // Flag
            ??? mov? eax, 1 // VPC function number
            ? }

            ??? // call VPC
            ?? _asm __emit 0Fh
            ?? _asm __emit 3Fh
            ?? _asm __emit 07h
            ?? _asm __emit 0Bh

            ? _asm
            ? {
            ??? mov eax, dword ptr ss:[esp]
            ??? mov dword ptr fs:[0], eax

            ??? add esp, 8

            ??? test ebx, ebx
            ???
            ??? setz al

            ??? lea esp, dword ptr ss:[ebp-4]
            ??? mov ebx, dword ptr ss:[esp]
            ??? mov ebp, dword ptr ss:[esp+4]

            ??? add esp, 8

            ??? jmp ret1
            exception_handler:
            ??? mov ecx, [esp+0Ch]
            ??? mov dword ptr [ecx+0A4h], -1 // EBX = -1 -> not running, ebx = 0 -> running
            ??? add dword ptr [ecx+0B8h], 4 // -> skip past the call to VPC
            ??? xor eax, eax // exception is handled
            ??? ret
            ?ret1:
            ??? ret
            ? }
            }

            3.12 FV_VME_RedPill
            ??? 這個(gè)方法似乎是檢測(cè)虛擬機(jī)的一個(gè)簡(jiǎn)單有效的方法,雖然還不能確定它是否是100%有效。名字很有意思,紅色藥丸(為什么不是bluepill,哈哈)。我在網(wǎng)上找到了個(gè)ppt專門介紹這個(gè)方法,可惜現(xiàn)在翻不到了。記憶中原理是這樣的,主要檢測(cè)IDT的數(shù)值,如果這個(gè)數(shù)值超過了某個(gè)數(shù)值,我們就可以認(rèn)為應(yīng)用程序處于虛擬環(huán)境中,似乎這個(gè)方法在多CPU的機(jī)器中并不可靠。據(jù)稱ScoobyDoo方法是RedPill的升級(jí)版。代碼也是在網(wǎng)上找的,做了點(diǎn)小改動(dòng)。有四種返回結(jié)果,可以確認(rèn)是VMWare,還是VirtualPC,還是其它VME,或是沒有處于VME中。
            ?? //return value:? 0:none,1:vmvare;2:vpc;3:others
            ?? unsigned char matrix[6];

            ??? unsigned char redpill[] =
            ??????? "\x0f\x01\x0d\x00\x00\x00\x00\xc3";

            ??? HANDLE hProcess = GetCurrentProcess();

            ??? LPVOID lpAddress = NULL;
            ??? PDWORD lpflOldProtect = NULL;

            ??? __try
            ??? {
            ??????? *((unsigned*)&redpill[3]) = (unsigned)matrix;

            ??????? lpAddress = VirtualAllocEx(hProcess, NULL, 6, MEM_RESERVE|MEM_COMMIT , PAGE_EXECUTE_READWRITE);
            ???????
            ??????? if(lpAddress == NULL)
            ??????????? return 0;

            ??????? BOOL success = VirtualProtectEx(hProcess, lpAddress, 6, PAGE_EXECUTE_READWRITE , lpflOldProtect);

            ??????? if(success != 0)
            ???????????? return 0;
            ??
            ??????? memcpy(lpAddress, redpill, 8);

            ??????? ((void(*)())lpAddress)();

            ??????? if (matrix[5]>0xd0)
            ??????? {
            ????????? if(matrix[5]==0xff)//vmvare
            ??????????? return 1;
            ????????? else if(matrix[5]==0xe8)//vitualpc
            ??????????? return 2;
            ????????? else
            ??????????? return 3;
            ??????? }
            ??????? else
            ??????????? return 0;
            ??? }
            ??? __finally
            ??? {
            ??????? VirtualFreeEx(hProcess, lpAddress, 0, MEM_RELEASE);
            ??? }

            四、? 檢測(cè)-斷點(diǎn)(FB_)
            這一部分內(nèi)容較少,但實(shí)際上可用的方法也比較多,我沒有深入研究,不敢亂寫,照抄了幾個(gè)常用的方法:
            //find breakpoint
            bool FB_HWBP_Exception();
            DWORD FB_SWBP_Memory_CRC();
            bool FB_SWBP_ScanCC(BYTE * addr,int len);
            bool FB_SWBP_CheckSum_Thread(BYTE *addr_begin,BYTE *addr_end,DWORD sumValue);

            4.1 FB_HWBP_Exception
            ? 在異常處理程序中檢測(cè)硬件斷點(diǎn),是比較常用的硬件斷點(diǎn)檢測(cè)方法。在很多地方都有提到。
            ? __asm
            ? {
            ??? push?? offset exeception_handler; set exception handler
            ??? push?? dword ptr fs:[0h]
            ??? mov??? dword ptr fs:[0h],esp?
            ??? xor??? eax,eax;reset EAX invoke int3
            ??? int??? 1h
            ??? pop??? dword ptr fs:[0h];restore exception handler
            ??? add??? esp,4
            ??? ;test if EAX was updated (breakpoint identified)
            ??? test?? eax,eax
            ??? jnz???? rt_label
            ??? jmp??? rf_label

            exeception_handler:
            ??? ;EAX = CONTEXT record
            ??? mov???? eax,dword ptr [esp+0xc]

            ??? ;check if Debug Registers Context.Dr0-Dr3 is not zero
            ??? cmp???? dword ptr [eax+0x04],0
            ??? jne???? hardware_bp_found
            ??? cmp???? dword ptr [eax+0x08],0
            ??? jne???? hardware_bp_found
            ??? cmp???? dword ptr [eax+0x0c],0
            ??? jne???? hardware_bp_found
            ??? cmp???? dword ptr [eax+0x10],0
            ??? jne???? hardware_bp_found
            ??? jmp???? exception_ret

            hardware_bp_found:
            ??? ;set Context.EAX to signal breakpoint found
            ??? mov???? dword ptr [eax+0xb0],0xFFFFFFFF
            exception_ret:
            ??? ;set Context.EIP upon return
            ??? inc?????? dword ptr [eax+0xb8];set ContextRecord.EIP
            ??? inc?????? dword ptr [eax+0xb8];set ContextRecord.EIP
            ??? xor???? eax,eax
            ??? retn
            ? }
            4.2 FB_SWBP_Memory_CRC()
            ? 由于在一些常用調(diào)試器中,比如OD,其是將代碼設(shè)置為0xcc來實(shí)現(xiàn)普通斷點(diǎn),因此當(dāng)一段代碼被設(shè)置了普通斷點(diǎn),則其中必定有代碼的修改。因此對(duì)關(guān)鍵代碼進(jìn)行CRC校驗(yàn)則可以實(shí)現(xiàn)偵測(cè)普通斷點(diǎn)。但麻煩的是每次代碼修改,或更換編譯環(huán)境,都要重新設(shè)置CRC校驗(yàn)值。
            ? 下面的代碼拷貝自《軟件加解密技術(shù)》,里面完成的是對(duì)整個(gè)代碼段的CRC校驗(yàn),CRC校驗(yàn)值保存在數(shù)據(jù)段。CRC32算法實(shí)現(xiàn)代碼網(wǎng)上有很多,就不列出來了。
            DWORD FB_SWBP_Memory_CRC()
            {
            ? //打開文件以獲得文件的大小
            ? DWORD fileSize,NumberOfBytesRW;
            ? DWORD CodeSectionRVA,CodeSectionSize,NumberOfRvaAndSizes,DataDirectorySize,ImageBase;
            ? BYTE* pMZheader;
            ? DWORD pPEheaderRVA;
            ? TCHAR? *pBuffer ;
            ? TCHAR szFileName[MAX_PATH];

            ? GetModuleFileName(NULL,szFileName,MAX_PATH);
            ? //打開文件
            ? HANDLE hFile = CreateFile(
            ??? szFileName,
            ??? GENERIC_READ,
            ??? FILE_SHARE_READ,
            ??? NULL,
            ??? OPEN_EXISTING,
            ??? FILE_ATTRIBUTE_NORMAL,
            ??? NULL);
            ?? if ( hFile != INVALID_HANDLE_VALUE )
            ?? {
            ??? //獲得文件長(zhǎng)度 :
            ??? fileSize = GetFileSize(hFile,NULL);
            ??? if (fileSize == 0xFFFFFFFF) return 0;
            ??? pBuffer = new TCHAR [fileSize];???? //// 申請(qǐng)內(nèi)存,也可用VirtualAlloc等函數(shù)申請(qǐng)內(nèi)存
            ??? ReadFile(hFile,pBuffer, fileSize, &NumberOfBytesRW, NULL);//讀取文件內(nèi)容
            ??? CloseHandle(hFile);? //關(guān)閉文件
            ?? }
            ?? else
            ???? return 0;
            ? pMZheader=(BYTE*)pBuffer; //此時(shí)pMZheader指向文件頭
            ? pPEheaderRVA = *(DWORD *)(pMZheader+0x3c);//讀3ch處的PE文件頭指針
            ? ///定位到PE文件頭(即字串“PE\0\0”處)前4個(gè)字節(jié)處,并讀出儲(chǔ)存在這里的CRC-32值:

            ? NumberOfRvaAndSizes=*((DWORD *)(pMZheader+pPEheaderRVA+0x74));//得到數(shù)據(jù)目錄結(jié)構(gòu)數(shù)量
            ? DataDirectorySize=NumberOfRvaAndSizes*0x8;//得到數(shù)據(jù)目錄結(jié)構(gòu)大小
            ? ImageBase=*((DWORD *)(pMZheader+pPEheaderRVA+0x34));//得到基地址
            ? //假設(shè)第一個(gè)區(qū)塊就是代碼區(qū)塊
            ? CodeSectionRVA=*((DWORD *)(pMZheader+pPEheaderRVA+0x78+DataDirectorySize+0xc));//得到代碼塊的RVA值
            ? CodeSectionSize=*((DWORD *)(pMZheader+pPEheaderRVA+0x78+DataDirectorySize+0x8));///得到代碼塊的內(nèi)存大小
            ? delete pBuffer;? // 釋放內(nèi)存
            ? return CRC32((BYTE*)(CodeSectionRVA+ImageBase),CodeSectionSize);
            }

            4.3 FB_SWBP_ScanCC
            掃描CC的方法,比照前面校驗(yàn)代碼CRC數(shù)值的方法更直接一些,它直接在所要檢測(cè)的代碼區(qū)域內(nèi)檢測(cè)是否有代碼被更改為0xCC,0xcc對(duì)應(yīng)匯編指令為int3 ,對(duì)一些常用的調(diào)試器(如OD)其普通斷點(diǎn)就是通過修改代碼為int3來實(shí)現(xiàn)的。但使用時(shí)要注意是否正常代碼中就包含CC。通常這個(gè)方法用于掃描API函數(shù)的前幾個(gè)字節(jié),比如檢測(cè)常用的MessageBoxA、GetDlgItemTextA等。

            bool FB_SWBP_ScanCC(BYTE * addr,int len)
            {
            ? FARPROC Func_addr ;
            ? HMODULE hModule = GetModuleHandle("USER32.dll");
            ? (FARPROC&) Func_addr =GetProcAddress ( hModule, "MessageBoxA");
            ? if (addr==NULL)
            ??? addr=(BYTE *)Func_addr;//for test
            ? BYTE tmpB;
            ? int i;
            ? __try
            ? {
            ??? for(i=0;i<len;i++,addr++)
            ??? {
            ????? tmpB=*addr;
            ????? tmpB=tmpB^0x55;
            ????? if(tmpB==0x99)// cmp 0xcc
            ??????? return true;
            ??? }
            ? }
            ? __except(1)
            ??? return false;
            ? return false;
            }
            ?
            4.4 FB_SWBP_CheckSum_Thread(BYTE *addr_begin,BYTE *addr_end,DWORD sumValue);
            此方法類似CRC的方法,只是這里是檢測(cè)累加和。它與CRC的方法有同樣的問題,就是要在編譯后,計(jì)算累加和的數(shù)值,再將該值保存到數(shù)據(jù)區(qū),重新編譯。在這里創(chuàng)建了一個(gè)單獨(dú)的線程用來監(jiān)視代碼段。
            DWORD WINAPI CheckSum_ThreadFunc( LPVOID lpParam )
            {
            ? DWORD dwThrdParam[3];
            ? BYTE tmpB;
            ? DWORD Value=0;
            ? dwThrdParam[0]=* ((DWORD *)lpParam);
            ???? dwThrdParam[1]=* ((DWORD *)lpParam+1);
            ????? dwThrdParam[2]=* ((DWORD *)lpParam+2);
            ? BYTE *addr_begin=(BYTE *)dwThrdParam[0];
            ? BYTE *addr_end=(BYTE *)dwThrdParam[1];
            ? DWORD sumValue=dwThrdParam[2];
            ? for(int i=0;i<(addr_end-addr_begin);i++)
            ??? Value=Value+*(addr_begin+i);
            ? /* //if sumvalue is const,it should be substract.
            ? DWORD tmpValue;
            ? Value=Value-(sumValue&0x000000FF);
            ? tmpValue=(sumValue&0x0000FF00)>>8;
            ? Value=Value-tmpValue;
            ? tmpValue=(sumValue&0x0000FF00)>>16;
            ? Value=Value-tmpValue;
            ? tmpValue=(sumValue&0x0000FF00)>>24;
            ? Value=Value-tmpValue;*/
            ? if (Value!=sumValue)
            ??? MessageBox(NULL,"SWBP is found by CheckSum_ThreadFunc","CheckSum_ThreadFunc",MB_OK|MB_ICONSTOP);
            ??? return 1;
            }

            bool FB_SWBP_CheckSum_Thread(BYTE *addr_begin,BYTE *addr_end,DWORD sumValue)
            {
            ??? DWORD dwThreadId;
            ? DWORD dwThrdParam[3];
            ? dwThrdParam[0]=(DWORD)addr_begin;
            ? dwThrdParam[1]=(DWORD)addr_end;
            ? dwThrdParam[2]=sumValue;
            ??? HANDLE hThread;

            ??? hThread = CreateThread(
            ??????? NULL,??????????????????????? // default security attributes
            ??????? 0,?????????????????????????? // use default stack size?
            ??????? CheckSum_ThreadFunc,???????? // thread function
            ??????? &dwThrdParam[0],??????????????? // argument to thread function
            ??????? 0,?????????????????????????? // use default creation flags
            ??????? &dwThreadId);??????????????? // returns the thread identifier
            ??? // Check the return value for success.
            ?
            ?? if (hThread == NULL)
            ????? return false;
            ?? else
            ?? {
            ????? Sleep(1000);
            ????? CloseHandle( hThread );
            ??? return true;
            ?? }
            }
            五、? 檢測(cè)-跟蹤(FT_)
            個(gè)人認(rèn)為,反跟蹤的一些技巧,多數(shù)不會(huì)非常有效,因?yàn)樵谡{(diào)試時(shí),多數(shù)不會(huì)被跟蹤經(jīng)過,除非用高超的技巧將關(guān)鍵代碼和垃圾代碼及這些反跟蹤技巧融合在一起,否則很容易被發(fā)現(xiàn)或被無意中跳過。
            函數(shù)列表如下:
            //Find Single-Step or Trace
            bool FT_PushSS_PopSS();
            void FT_RDTSC(unsigned int * time);
            DWORD FT_GetTickCount();
            DWORD FT_SharedUserData_TickCount();
            DWORD FT_timeGetTime();
            LONGLONG FT_QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
            bool FT_F1_IceBreakpoint();
            bool FT_Prefetch_queue_nop1();
            bool FT_Prefetch_queue_nop2();

            5.1 FT_PushSS_PopSS
            這個(gè)反調(diào)試在<<windows anti-debug reference>>里有描述,如果調(diào)試器跟蹤經(jīng)過下面的指令序列:
            ? __asm
            ? {
            ??? push ss??? //反跟蹤指令序列
            ??? ;junk
            ??? pop? ss??? //反跟蹤指令序列
            ??? pushf??? //反跟蹤指令序列
            ??? ;junk
            ??? pop eax??? //反跟蹤指令序列
            }
            Pushf將會(huì)被執(zhí)行,同時(shí)調(diào)試器無法設(shè)置壓進(jìn)堆棧的陷阱標(biāo)志,應(yīng)用程序通過檢測(cè)陷阱標(biāo)志就可以判斷處是否被跟蹤調(diào)試。

            ? __asm
            ? {
            ??? push ebp
            ??? mov ebp,esp
            ??? push ss??? //反跟蹤指令序列
            ??? ;junk
            ??? pop? ss??? //反跟蹤指令序列
            ??? pushf??? //反跟蹤指令序列
            ??? ;junk
            ??? pop eax??? //反跟蹤指令序列
            ??? and? eax,0x00000100
            ??? jnz? rt_label

            ??? xor eax,eax
            ??? mov esp,ebp
            ??? pop ebp
            ??? retn
            rt_label:
            ??? xor eax,eax
            ??? inc eax
            ??? mov esp,ebp
            ??? pop ebp
            ??? retn
            ? }

            5.2 FT_RDTSC
            通過檢測(cè)某段程序執(zhí)行的時(shí)間間隔,可以判斷出程序是否被跟蹤調(diào)試,被跟蹤調(diào)試的代碼通常都有較大的時(shí)間延遲,檢測(cè)時(shí)間間隔的方法有很多種。比如RDTSC指令,kernel32_GetTickCount函數(shù),winmm_ timeGetTime 函數(shù)等等。
            下面為RDTSC的實(shí)現(xiàn)代碼。

            ? int time_low,time_high;
            ? __asm
            ? {
            ??? rdtsc
            ??? mov??? time_low,eax
            ??? mov??? time_high,edx
            ? }

            5.3 FT_GetTickCount
            ? GetTickCount函數(shù)檢測(cè)時(shí)間間隔簡(jiǎn)單且常用。直接調(diào)用即可。具體可查MSDN。

            5.4 FT_SharedUserData_TickCount
            ? 直接調(diào)用GetTickCount函數(shù)來檢測(cè)時(shí)間間隔的方法,雖然簡(jiǎn)單卻容易被發(fā)現(xiàn)。而使用GetTickCount的內(nèi)部實(shí)現(xiàn)代碼,直接讀取SharedUserData數(shù)據(jù)結(jié)構(gòu)里的數(shù)據(jù)的方法,更隱蔽些。下面的代碼是直接從GetTickCount里扣出來的,其應(yīng)該是在位于0x7FFE0000地址處的SharedUserData數(shù)據(jù)接口里面直接取數(shù)據(jù),不過這個(gè)代碼應(yīng)該有跨平臺(tái)的問題,我這里沒有處理。大家可以完善下。
            ? DWORD tmpD;
            ? __asm
            ? {
            ??? mov???? edx, 0x7FFE0000
            ??? mov???? eax, dword ptr [edx]
            ??? mul???? dword ptr [edx+4]
            ??? shrd??? eax, edx, 0x18
            ??? mov??? tmpD,eax
            ? }
            ? return tmpD;

            5.5 FT_timeGetTime
            ? 使用winmm里的timeGetTime的方法也可以用來檢測(cè)時(shí)間間隔。直接調(diào)用這個(gè)函數(shù)即可。具體可查MSDN。

            5.6 FT_QueryPerformanceCounter
            ? 這是一種高精度時(shí)間計(jì)數(shù)器的方法,它的檢測(cè)刻度最小,更精確。
            ? if(QueryPerformanceCounter(lpPerformanceCount))
            ??????? return lpPerformanceCount->QuadPart;
            ? else
            ???? return 0;

            5.7 FT_F1_IceBreakpoint
            ? 在<<Windows anti-debug reference>>中有講述這個(gè)反跟蹤技巧。這個(gè)所謂的"Ice breakpoint" 是Intel 未公開的指令之一, 機(jī)器碼為0xF1.執(zhí)行這個(gè)指令將產(chǎn)生單步異常.,如果程序已經(jīng)被跟蹤, 調(diào)試器將會(huì)以為它是通過設(shè)置標(biāo)志寄存器中的單步標(biāo)志位生成的正常異常. 相關(guān)的異常處理器將不會(huì)被執(zhí)行到.下面是我的實(shí)現(xiàn)代碼:

            __asm
            ? {
            ? push?? offset eh_f1; set exception handler
            ???? push? dword ptr fs:[0h]
            ???? mov??? dword ptr fs:[0h],esp?
            ???? xor?? eax,eax;reset EAX invoke int3
            ???? _emit 0xf1
            ???? pop??? dword ptr fs:[0h];restore exception handler
            ???? add??? esp,4
            ? test? eax,eax
            ? jz??? rt_label_f1
            ? jmp??? rf_label_f1

            eh_f1:
            ???? mov eax,dword ptr[esp+0xc]
            ? mov??? dword ptr [eax+0xb0],0x00000001;set flag (ContextRecord.EAX)
            ???? inc dword ptr [eax+0xb8]
            ???? xor eax,eax
            ???? retn
            rt_label_f1:
            ? inc??? eax
            ? mov??? esp,ebp
            ???? pop??? ebp
            ???? retn
            rf_label_f1:
            ? xor??? eax,eax
            ? mov??? esp,ebp
            ???? pop??? ebp
            ???? retn
            ? }

            ?
            5.8 FT_Prefetch_queue_nop1
            這個(gè)反調(diào)試是在<<ANTI-UNPACKER TRICKS>>中給出的,它主要是基于REP指令,通過REP指令來修改自身代碼,在非調(diào)試態(tài)下,計(jì)算機(jī)會(huì)將該指令完整取過來,因此可以正確的執(zhí)行REP這個(gè)指令,將自身代碼完整修改,但在調(diào)試態(tài)下,則在修改自身的時(shí)候立即跳出。
            這個(gè)反跟蹤技巧個(gè)人覺得用處不大,因?yàn)橹挥性赗EP指令上使用F7單步時(shí),才會(huì)觸發(fā)這個(gè)反跟蹤,而我個(gè)人在碰到REP時(shí),通常都是F8步過。下面是利用這個(gè)CPU預(yù)取指令的特性的實(shí)現(xiàn)反跟蹤的一種方法,正常情況下,REP指令會(huì)修改其后的跳轉(zhuǎn)指令,進(jìn)入正常的程序流程,但在調(diào)試態(tài)下,其無法完成對(duì)其后代碼的修改,從而實(shí)現(xiàn)反調(diào)試。

            ?? DWORD oldProtect;
            ?? DWORD tmpProtect;
            ?? __asm
            ?? {
            ??? lea eax,dword ptr[oldProtect]
            ??? push eax
            ??? push 0x40
            ??? push 0x10
            ??? push offset label3;
            ??? call dword ptr [VirtualProtect];
            ??? nop
            label3:
            ??? mov al,0x90
            ??? push 0x10
            ??? pop ecx
            ??? mov edi,offset label3
            ??? rep stosb
            ??? jmp rt_label
            ??? nop
            ??? nop
            ??? nop
            ??? nop
            ??? nop
            rf_label:
            ??? ;write back
            ??? mov dword ptr[label3],0x106a90b0
            ??? mov dword ptr[label3+0x4],0x205CBF59
            ??? mov dword ptr[label3+0x8],0xAAF30040
            ??? mov dword ptr[label3+0xc],0x90909090
            ??? mov dword ptr[label3+0x6],offset label3
            ??? lea eax, dword ptr[tmpProtect];
            ??? ;restore protect
            ??? push eax
            ??? push oldProtect
            ??? push 0x10
            ??? push offset label3;
            ??? call dword ptr [VirtualProtect];

            ??? xor eax,eax
            ??? mov esp,ebp
            ??? pop ebp
            ??? retn
            rt_label:
            ??? ;write back
            ??? mov dword ptr[label3],0x106a90b0
            ??? mov dword ptr[label3+0x4],0x205CBF59
            ??? mov dword ptr[label3+0x8],0xAAF30040
            ??? mov dword ptr[label3+0xc],0x90909090
            ??? mov dword ptr[label3+0x6],offset label3
            ??? lea eax, dword ptr[tmpProtect];
            ??? ;restore protect
            ??? push eax
            ??? push oldProtect
            ??? push 0x10
            ??? push offset label3;
            ??? call dword ptr [VirtualProtect];

            ??? xor eax,eax
            ??? inc eax
            ??? mov esp,ebp
            ??? pop ebp
            ??? retn
            ? }


            5.9 FT_Prefetch_queue_nop2
            ? 與5.8節(jié)類似,這是根據(jù)CPU預(yù)取指令的這個(gè)特性實(shí)現(xiàn)的另一種反跟蹤技巧。原理是通過檢測(cè)REP指令后的ECX值,來判斷REP指令是否被完整執(zhí)行。在正常情況下,REP指令完整執(zhí)行后,ECX值應(yīng)為0;但在調(diào)試態(tài)下,由于REP指令沒有完整執(zhí)行,ECX值為非0值。通過檢測(cè)ECX值,實(shí)現(xiàn)反跟蹤。
            ? DWORD oldProtect;
            ? DWORD tmpProtect;
            ? __asm
            ? {
            ??? lea eax,dword ptr[oldProtect]
            ??? push eax
            ??? push 0x40
            ??? push 0x10
            ??? push offset label3;
            ??? call dword ptr [VirtualProtect];
            ??? mov ecx,0
            label3:
            ??? mov al,0x90
            ??? push 0x10
            ??? pop ecx
            ??? mov edi,offset label3
            ??? rep stosb
            ??? nop
            ??? nop
            ??? nop
            ??? nop
            ??? nop
            ??? nop
            ??? push ecx
            ??? ;write back
            ??? mov dword ptr[label3],0x106a90b0
            ??? mov dword ptr[label3+0x4],0x201CBF59
            ??? mov dword ptr[label3+0x8],0xAAF30040
            ??? mov dword ptr[label3+0xc],0x90909090
            ??? mov dword ptr[label3+0x6],offset label3
            ??? lea eax, dword ptr[tmpProtect];
            ??? ;restore protect
            ??? push eax
            ??? push oldProtect
            ??? push 0x10
            ??? push offset label3;
            ??? call dword ptr [VirtualProtect];
            ??? pop ecx

            ??? test ecx,ecx
            ??? jne rt_label
            ? }
            rf_label:
            ? return false;
            rt_label:
            ? return true;

            六、? 檢測(cè)-補(bǔ)丁(FP_)
            這部分內(nèi)容也較少,方法當(dāng)然也有很多種,原理都差不多,我只選了下面三種。這幾種方法通常在一些殼中較常用,用于檢驗(yàn)文件是否被脫殼或被惡意修改。
            函數(shù)列表如下:
            //find Patch
            bool FP_Check_FileSize(DWORD Size);
            bool FP_Check_FileHashValue_CRC(DWORD CRCVALUE_origin);
            bool FP_Check_FileHashValue_MD5(DWORD MD5VALUE_origin);

            6.1 FP_Check_FileSize(DWORD Size)
            ? 通過檢驗(yàn)文件自身的大小的方法,是一種比較簡(jiǎn)單的文件校驗(yàn)方法,通常如果被脫殼,或被惡意修改,就可能影響到文件的大小。我用下面的代碼實(shí)現(xiàn)。需注意的是,文件的大小要先編譯一次,將首次編譯得到的數(shù)值寫入代碼,再重新編譯完成。
            ? DWORD Current_Size;
            ? TCHAR szPath[MAX_PATH];
            ? HANDLE hFile;

            ? if( !GetModuleFileName( NULL,szPath, MAX_PATH ) )
            ??????? return FALSE;

            ? hFile = CreateFile(szPath,
            ??? GENERIC_READ ,
            ??? FILE_SHARE_READ,
            ??? NULL,
            ??? OPEN_ALWAYS,
            ??? FILE_ATTRIBUTE_NORMAL,
            ??? NULL);
            ? if (hFile == INVALID_HANDLE_VALUE)
            ??? return false;
            ? Current_Size=GetFileSize(hFile,NULL);
            ? CloseHandle(hFile);
            ? if(Current_Size!=Size)
            ??? return true;
            ? return false;

            6.2 FP_Check_FileHashValue_CRC
            ? 檢驗(yàn)文件的CRC數(shù)值,是比較常用的文件校驗(yàn)方法,相信很多人都碰到過了,我是在《軟件加解密技術(shù)》中了解到的。需注意的是文件原始CRC值的獲得,及其放置位置,代碼編寫完成后,通常先運(yùn)行一遍程序,使用調(diào)試工具獲得計(jì)算得到的數(shù)值,在將這個(gè)數(shù)值寫入文件中,通常這個(gè)數(shù)值不參加校驗(yàn),可以放置在文件的尾部作為附加數(shù)據(jù),也可以放在PE頭中不用的域中。
            ? 下面的代碼只是個(gè)演示,沒有保存CRC的真實(shí)數(shù)值,也沒有單獨(dú)存放。

            ? DWORD fileSize,NumberOfBytesRW;
            ? DWORD CRCVALUE_current;
            ? TCHAR szFileName[MAX_PATH];
            ? TCHAR? *pBuffer ;
            ? GetModuleFileName(NULL,szFileName,MAX_PATH);
            ? HANDLE hFile = CreateFile(
            ??? szFileName,
            ??? GENERIC_READ,
            ??? FILE_SHARE_READ,
            ??? NULL,
            ??? OPEN_EXISTING,
            ??? FILE_ATTRIBUTE_NORMAL,
            ??? NULL);
            ? if (hFile != INVALID_HANDLE_VALUE )
            ? {
            ??? fileSize = GetFileSize(hFile,NULL);
            ??? if (fileSize == 0xFFFFFFFF) return false;
            ??? pBuffer = new TCHAR [fileSize];?
            ??? ReadFile(hFile,pBuffer, fileSize, &NumberOfBytesRW, NULL);
            ??? CloseHandle(hFile);
            ? }
            ? CRCVALUE_current=CRC32((BYTE *)pBuffer,fileSize);
            ? if(CRCVALUE_origin!=CRCVALUE_current)
            ??? return true;
            ? return false;

            6.3 FP_Check_FileHashValue_MD5
            與6.2節(jié)的原理相同,只是計(jì)算的是文件的MD5數(shù)值。仍要注意6.2節(jié)中同樣的MD5真實(shí)數(shù)值的獲得和存放問題。

            未完。

            本文來自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/hack8/archive/2009/01/18/3826322.aspx

            免费无码国产欧美久久18| 久久se这里只有精品| 久久天天躁狠狠躁夜夜不卡| 久久天天躁夜夜躁狠狠| 精品国产乱码久久久久久郑州公司 | 久久精品麻豆日日躁夜夜躁| 久久亚洲日韩精品一区二区三区| 精品久久久久久久| 亚洲精品美女久久久久99小说| 无码AV波多野结衣久久| 国产成人精品久久亚洲高清不卡 | 香蕉99久久国产综合精品宅男自| 久久久久久亚洲Av无码精品专口 | 伊色综合久久之综合久久| 久久夜色精品国产欧美乱| 狠狠久久综合伊人不卡| 亚洲国产精品无码成人片久久| 国产69精品久久久久99| 色婷婷综合久久久中文字幕| 久久婷婷五月综合色99啪ak| 国产精品久久永久免费| 久久亚洲美女精品国产精品| 青春久久| 久久最新免费视频| 国产精品一久久香蕉国产线看| 2021最新久久久视精品爱| 国产免费久久久久久无码| 蜜臀久久99精品久久久久久小说| 久久99热这里只有精品66| 久久久久国产一区二区| 久久99精品久久久久久噜噜 | 亚洲综合精品香蕉久久网| 久久久久亚洲av成人无码电影| 91精品观看91久久久久久| 久久国产精品无码一区二区三区 | 久久精品国产免费| 精品久久8x国产免费观看| 国产亚洲婷婷香蕉久久精品| 国产精品99久久精品| 97热久久免费频精品99| AV无码久久久久不卡网站下载 |