• 标 题:关于StyleXP反跟踪代码的分析以及WinDbg简单教学 (7千字)
  • 作 者:leo_cyl1
  • 时 间:2002-4-1 17:57:25
  • 链 接:http://bbs.pediy.com

目标软件:Style XP 0.9 beta RC2
目标文件:StyleXP.exe
使用环境:WINDOW XP
使用工具:WinDbg,wasm32

下载地址:http://www.tgtsoft.com/download.html

    Style XP是更改WINDOW XP“皮肤”的工具。通过使用皮肤,它可以改变Windows的外观界面,而且还提供

了皮肤制作软件。下载安装的过程非常简便,在机器重启后变可令你Win XP外观焕然一新,整体感觉要比

Winblinds看上去要轻盈淡雅。未注册只能用30天。

    Style XP的反跟踪代码做的很有特色,虽然比不上aspr等“大牌”程序,但只花费了很小的代价,就基本

达到的anit-debug的效果。
 
    因为刚装好window xp,还没调试好soft-ice,只好用DDK工具集里的WinDbg了。(其实WinDbg的功能一点

也不比soft-ice逊色,还可以一边调试一边看MSDN的帮助文挡)


打开WinDbg,选File->Open Executable,装入StyleXP.exe,f5执行后,被中断在一条int3的指令上:

* Reference To: COMCTL32.InitCommonControls, Ord:0011h
                                  |
:00422FA4 8B35E0404B00            mov esi, dword ptr [004B40E0]
:00422FAA FFD6                    call esi
:00422FAC 57                      push edi
:00422FAD E81DFA0200              call 004529CF
:00422FB2 59                      pop ecx
:00422FB3 8B4508                  mov eax, dword ptr [ebp+08]
:00422FB6 8B80C8000000            mov eax, dword ptr [eax+000000C8]
:00422FBC 8945E0                  mov dword ptr [ebp-20], eax
:00422FBF CC                      int 03    《==========中断在这里
:00422FC0 FFD6                    call esi
:00422FC2 57                      push edi
:00422FC3 E807FA0200              call 004529CF

原来是用SEH来实现anti-debug的,那好办,下命令“SXD *”这条命令是让WinDbg忽略所有的异常,只在debug

窗口输出信息。类似与soft-ice中的“faults off”命令。再下命令“GN”,即“Go Unhandled Exception”

(执行,但不处理异常情况)满以为搞定了,谁知程序居然自动退出了!看来还有暗桩。重新装入StyleXP,一

步一步跟踪来到这里:


:00422AA4 8D86CC000000            lea eax, dword ptr [esi+000000CC]
:00422AAA 8BCE                    mov ecx, esi
:00422AAC 50                      push eax
:00422AAD E8DB200800              call 004A4B8D

* Reference To: KERNEL32.GetTickCount, Ord:0186h
                                  |
:00422AB2 8B1D28444B00            mov ebx, dword ptr [004B4428]
:00422AB8 FFD3                    call ebx
:00422ABA 8986C0000000            mov dword ptr [esi+000000C0], eax


以上代码call KERNEL32.GetTickCount 将当前的时间保存在[esi+000000C0]中

* Reference To: ole32.CoUninitialize, Ord:0059h
                                  |
:00422AC0 FF15F8494B00            Call dword ptr [004B49F8]
:00422AC6 8D45DC                  lea eax, dword ptr [ebp-24]
:00422AC9 50                      push eax
:00422ACA 57                      push edi
:00422ACB 56                      push esi
:00422ACC 68722F4200              push 00422F72
:00422AD1 6800000100              push 00010000
:00422AD6 57                      push edi

* Reference To: KERNEL32.CreateThread, Ord:004Dh
                                  |
:00422AD7 FF153C444B00            Call dword ptr [004B443C]
:00422ADD 3BC7                    cmp eax, edi
:00422ADF 8945F0                  mov dword ptr [ebp-10], eax
:00422AE2 7416                    je 00422AFA
:00422AE4 6AFF                    push FFFFFFFF
:00422AE6 50                      push eax

* Reference To: KERNEL32.WaitForSingleObject, Ord:02FDh
                                  |
:00422AE7 FF152C444B00            Call dword ptr [004B442C]


然后产生一个子线程(子线程地址为00422F72),再将本身的主线程挂起,直到子线程结束。(子线程的作用

后面有解释)


:00422AED 8D45E0                  lea eax, dword ptr [ebp-20]
:00422AF0 50                      push eax
:00422AF1 FF75F0                  push [ebp-10]

* Reference To: KERNEL32.GetExitCodeThread, Ord:011Fh
                                  |
:00422AF4 FF1538444B00            Call dword ptr [004B4438]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00422AE2(C)
|
:00422AFA 817DE0E8030000          cmp dword ptr [ebp-20], 000003E8
:00422B01 7732                    ja 00422B35
:00422B03 FFD3                    call ebx      〈===KERNEL32.GetTickCount
:00422B05 2B86C0000000            sub eax, dword ptr [esi+000000C0]
:00422B0B 3D983A0000              cmp eax, 00003A98  〈== 3a98(hex)就是15000(dec)
:00422B10 7723                    ja 00422B35

上面代码再次call KERNEL32.GetTickCount,将当前时间和刚才的比较,如果大于15秒就over。


:00422B12 E8EC1C0800              call 004A4803
:00422B17 8B4008                  mov eax, dword ptr [eax+08]
:00422B1A 57                      push edi
:00422B1B 50                      push eax
:00422B1C 68BF304200              push 004230BF
:00422B21 6A09                    push 00000009

* Reference To: USER32.SetWindowsHookExA, Ord:0267h
                                  |
:00422B23 FF155C484B00            Call dword ptr [004B485C]
:00422B29 817DE0E8030000          cmp dword ptr [ebp-20], 000003E8
:00422B30 8945F0                  mov dword ptr [ebp-10], eax
:00422B33 7607                    jbe 00422B3C


产生“钩子”程序(WH_DEBUG)


* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004227A2(U), :004229A6(U), :00422B01(C), :00422B10(C)
|
:00422B35 33C0                    xor eax, eax
:00422B37 E971010000              jmp 00422CAD

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00422B33(C)
|
:00422B3C FFD3                    call ebx
:00422B3E 2B86C0000000            sub eax, dword ptr [esi+000000C0]
:00422B44 3D983A0000              cmp eax, 00003A98
:00422B49 760E                    jbe 00422B59

再次call KERNEL32.GetTickCount,将当前时间和刚才的比较,如果大于15秒就over。

将上面代码总结一下:
1)记录一个时间点。
2)产生一个子线程,并且再将本身的主线程挂起,直到子线程结束。
3)在子线程中计算是否注册以及是否过期,并且使用SEH技术,用int3断点指令触发debuger(如果有的话),

这时会有时间上的延迟。

4)当子线程退出,比较第一步的时间点,如果大于15秒,就说明本身被调试。(因为调试时要下断点,会有时

间延迟)

要去掉暗装也容易,将cmp eax, 00003A98该为xor eax,eax即可。这样就可以调试子线程了。
因为注册码比较部分挺复杂,我用爆破的方法使它永远不过期。
下断点“BP GetSystemTime”很容易找到关键:

:00478283 8B4C2410                mov ecx, dword ptr [esp+10]
:00478287 83C0FC                  add eax, FFFFFFFC
:0047828A 83C104                  add ecx, 00000004
:0047828D 50                      push eax
:0047828E 51                      push ecx
:0047828F 8B4C241C                mov ecx, dword ptr [esp+1C]
:00478293 E8A8150000              call 00479840
:00478298 8B4C2410                mov ecx, dword ptr [esp+10]
:0047829C 3B01                    cmp eax, dword ptr [ecx]
:0047829E 7412                    je 004782B2  〈==这里判断是否将时间改回原来
:004782A0 BE090404C0              mov esi, C0040409
:004782A5 56                      push esi
:004782A6 6A00                    push 00000000
:004782A8 682A070000              push 0000072A
:004782AD E929040000              jmp 004786DB


以下代码计算过期的时间
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0047829E(C)
|
:004782B2 668B4104                mov ax, word ptr [ecx+04]
:004782B6 668BD0                  mov dx, ax
:004782B9 8BC8                    mov ecx, eax
:004782BB 66C1EA09                shr dx, 09〈==dx为年份
:004782BF C1E905                  shr ecx, 05
:004782C2 81C2D0070000            add edx, 000007D0  〈==年份加上2000
:004782C8 83E01F                  and eax, 0000001F  〈===eax为天
:004782CB 83E10F                  and ecx, 0000000F  〈ecx==为月
:004782CE 668954241C              mov word ptr [esp+1C], dx
:004782D3 6689442420              mov word ptr [esp+20], ax
:004782D8 8B542420                mov edx, dword ptr [esp+20]
:004782DC 66894C241E              mov word ptr [esp+1E], cx
:004782E1 8B44241E                mov eax, dword ptr [esp+1E]
:004782E5 8B4C241C                mov ecx, dword ptr [esp+1C]



在:004782C2 处将edx的值该为一个很大的数就可以了。