软件地址:  http://goldsun.ys168.com 对外公布\mjcs_ori_unpack.zip 1.5MB
文章出自:PeaceClub Goldsun[at]84823714[dot]qq
工具: Ollydbg,kerberos API spy,Imprec,HideOD

情况说明:一个朋友让我检查一下此软件是否有后门,所以就拿来研究了一下.
分析:Peid一看是未知壳,而且是自创进程型的.用ollydbg载入,发现有anti,所以用了看雪的HideOD插件,运行

后主进程ExitProcess退出,CreateProcessA创建的子进程运行中.
POEP:

引用:

00575000 > 74 07            JE SHORT mjcs.00575009
00575002   75 05            JNZ SHORT mjcs.00575009
00575004   1932             SBB DWORD PTR DS:[EDX],ESI
00575006   67:E8 E8741F75   CALL 7576C4F4                            ; Superfluous prefix
0057500C   1D E8683944      SBB EAX,443968E8
00575011   CD 00            INT 0
00575013   59               POP ECX
00575014   9C               PUSHFD
00575015   50               PUSH EAX
00575016   74 0A            JE SHORT mjcs.00575022
00575018   75 08            JNZ SHORT mjcs.00575022
0057501A   E8 59C20400      CALL 005C1278
0057501F   55               PUSH EBP
00575020   8BEC             MOV EBP,ESP
00575022   E8 F4FFFFFF      CALL mjcs.0057501B
00575027   56               PUSH ESI
00575028   57               PUSH EDI
00575029   53               PUSH EBX
0057502A   78 0F            JS SHORT mjcs.0057503B
0057502C   79 0D            JNS SHORT mjcs.0057503B
0057502E   E8 34994749      CALL 499EE967
00575033   34 33            XOR AL,33


不知道什么壳,花指令多。

第一步:通过偷窥设法转单进程
想看看此软件到底通过什么标志来进行进程转换.所以偶用
kerberos API spy调入程序,运行后得到log:

引用:

 This file generated by kerberos API spy v1.02
(C) 2004-2005 Rustem Fasihov  <fasihov@mail.ru>

Sat Dec 10 9:47:03 2005

Plugin not installed

Analyzing database...
OK...
Installing hooks
OK...


DLL's in database:               2
DLL's failed to load:            0
Functions in database:           1312
Functions failed to initialize:  0

======================================================================================

======================================================================================

============================
MODULE_NAME    RET_ADDR   FUNCTION_NAME
======================================================================================

======================================================================================

============================
kernel32.dll | 7C801BE6 | HeapFree(00250000, 00000000, 00253370) returns: 00253901
msvcrt.dll   | 77C09FC2 | TlsGetValue(00000001) returns: 003E1E90
msvcrt.dll   | 77BFC2DE | HeapFree(003E0000, 00000000, 003E1E90) returns: 00000001
msvcrt.dll   | 77C0A0E8 | TlsSetValue(00000001, 00000000) returns: 00000001
IMM32.DLL    | 763027A3 | GetKeyboardLayout(00000000) returns: 08040804
ntdll.dll    | 7C948571 | HeapFree(00250000, 00000000, 00253028) returns: 00000001
ntdll.dll    | 7C948589 | HeapFree(00250000, 00000000, 00253018) returns: 00000001
ntdll.dll    | 7C938DAB | TryEnterCriticalSection(7C99C0D8) returns: 00000001
ntdll.dll    | 7C948619 | HeapAlloc(00250000, 000C0000, 00000004) returns: 00253018
ntdll.dll    | 7C948662 | HeapAlloc(00250000, 000C0000, 00000014) returns: 00253028
kernel32.dll | 7C80B5B1 | GetModuleHandleA(00000000) returns: 00400000
msvcrt.dll   | 77BEF0C0 | TlsGetValue(00000001) returns: 00000000
msvcrt.dll   | 77BFC1DB | HeapAlloc(003E0000, 00000008, 00000088) returns: 003E1E90
msvcrt.dll   | 77BEF0E5 | TlsSetValue(00000001, 003E1E90) returns: 00000001
msvcrt.dll   | 77BEF0F6 | GetCurrentThreadId() returns: 00000B40
ntdll.dll    | 7C957942 | TryEnterCriticalSection(7C99C0D8) returns: 00000001
mjcs.exe     | 0057CA5E | LoadLibraryA(0057BA7E: "kernel32.dll") returns: 7C800000
mjcs.exe     | 0057CA81 | LoadLibraryA(0057BA7E: "kernel32.dll") returns: 7C800000
mjcs.exe     | 0057B801 | GetProcAddress(7C800000, 0057C8CE: "HeapAlloc") returns: 7C9305D4
mjcs.exe     | 0057CAA1 | GetProcessHeap() returns: 00250000
mjcs.exe     | 0057CAA4 | HeapAlloc(00250000, 00000008, 00000320) returns: 00253048
mjcs.exe     | 0057C7A9 | GetModuleHandleA(0057BA7E: "kernel32.dll") returns: 7C800000
mjcs.exe     | 0057CB03 | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057C7C2 | GetModuleHandleA(0057ECC6: "kernel32.dll") returns: 7C800000
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057B801 | GetProcAddress(7C800000, 0057EEB6: "SetLastError") returns: 7C930340
mjcs.exe     | 0057B801 | GetProcAddress(7C800000, 0057EFA4: "HeapAlloc") returns: 7C9305D4
mjcs.exe     | 0057B801 | GetProcAddress(7C800000, 0057EFBD: "HeapFree") returns: 7C93043D
mjcs.exe     | 0057B801 | GetProcAddress(7C800000, 0057F06A: "GetLastError") returns: 7C930331
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057C7C2 | GetModuleHandleA(0057ECD3: "user32.dll") returns: 77D10000
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057C7C2 | GetModuleHandleA(0057ECDE: "advapi32.dll") returns: 77DA0000
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057B47B | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057B840 | GetProcessHeap() returns: 00250000
mjcs.exe     | 0057B846 | HeapAlloc(00250000, 00000008, 0000000C) returns: 002552E8
mjcs.exe     | 0057B86B | GetProcessHeap() returns: 00250000
mjcs.exe     | 0057B871 | HeapAlloc(00250000, 00000008, 00004000) returns: 00255300
mjcs.exe     | 0057B89E | VirtualProtect(00255300, 00004000, 00000040, 0012FFB8) returns: 00000001
mjcs.exe     | 00575437 | GetVersion() returns: 0A280105
mjcs.exe     | 0057C2F9 | GetModuleHandleA(00000000) returns: 00400000
ntdll.dll    | 7C957942 | TryEnterCriticalSection(7C99C0D8) returns: 00000001
mjcs.exe     | 005759D1 | GetTickCount() returns: 00401EDA
mjcs.exe     | 00575834 | GetTickCount() returns: 00401EDA
mjcs.exe     | 00575869 | GetTickCount() returns: 00401EDA
ntdll.dll    | 7C957942 | TryEnterCriticalSection(7C99C0D8) returns: 00000001
mjcs.exe     | 00575A91 | GetTickCount() returns: 00401EDA
mjcs.exe     | 0057C48E | GetVersionExA(0012FF2C) returns: 00000000
mjcs.exe     | 0057C2F9 | GetModuleHandleA(00000000) returns: 00400000
mjcs.exe     | 0057D369 | GetVersion() returns: 0A280105
mjcs.exe     | 0057D38B | LoadLibraryA(0057D353: "Kernel32.dll") returns: 7C800000
mjcs.exe     | 0057D3B3 | GetProcessTimes(FFFFFFFF, 0012FFB8, 0012FFB0, 0012FFB0, 0012FFB0) 

returns: 00000001
mjcs.exe     | 0057D3C1 | GetSystemTimeAsFileTime(0012FFB0) returns: 01C5FD2B
mjcs.exe     | 0057D53B | GetVersion() returns: 0A280105
mjcs.exe     | 0057D549 | GetTickCount() returns: 00401EDA
mjcs.exe     | 0057D566 | LoadLibraryA(0057D52B: "ntdll.dll") returns: 7C920000
mjcs.exe     | 0057C572 | wsprintfA(0012FE8C) returns: 00000014
mjcs.exe     | 0057C584 | GetTempPathA(00000104, 0012FEBC) returns: 00000023
mjcs.exe     | 0057C5B2 | CreateFileA(0012FEBC: "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\~temp0-

26013477...", C0000000, 00000003, 00000000, 00000004, 00000002, 00000000) returns: 00000010
mjcs.exe     | 0057C5D1 | ReadFile(00000010, 0012FE80, 00000004, 0012FE88, 00000000) returns: 

00000001
mjcs.exe     | 0057C5E8 | GetTickCount() returns: 00401EDA
mjcs.exe     | 0057C61C | SetFilePointer(00000010, 00000000, 00000000, 00000000) returns: 00000000
mjcs.exe     | 0057C630 | WriteFile(00000010, 0012FE80, 00000004, 0012FE88, 00000000) returns: 

00000001
mjcs.exe     | 0057C6C3 | CloseHandle(00000010) returns: 00000001
mjcs.exe     | 00575D53 | GetCommandLineA() returns: 00252398
mjcs.exe     | 00575DBF | CreateProcessA(00000000, 0057524C: ""C:\Documents and 

Settings\Administrator\", 00000000, 00000000, 00000001, 00000000, 00000000, 00000000, 00575350, 

0057523C) returns: 00000001
mjcs.exe     | 00575DD7 | ExitProcess(00000000) 


C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\~temp0
哦,原来通过临时文件来做Flag.  
我们在CreateProcessA往上看,找标志,强行转单进程
ollydbg载入,he 575d53 (GetCommandLineA后,要用硬件断点,壳本身检测5个长度的cc)
中断后向上观察:

引用:

00575D2E   75 07            JNZ SHORT mjcs.00575D37
00575D30   74 05            JE SHORT mjcs.00575D37
00575D32   0010             ADD BYTE PTR DS:[EAX],DL
00575D34   40               INC EAX
00575D35   00E8             ADD AL,CH
00575D37   0F84 9A000000    JE mjcs.00575DD7 //*****注意这里****
00575D3D   E8 01000000      CALL mjcs.00575D43
00575D42   FF58 05          CALL FAR FWORD PTR DS:[EAX+5]            ; Far call
00575D45   1100             ADC DWORD PTR DS:[EAX],EAX
00575D47   0000             ADD BYTE PTR DS:[EAX],AL
00575D49   50               PUSH EAX
00575D4A   E9 77890000      JMP mjcs.0057E6C6
00575D4F   0010             ADD BYTE PTR DS:[EAX],DL
00575D51   40               INC EAX
00575D52   0050 8B          ADD BYTE PTR DS:[EAX-75],DL
00575D55   F8               CLC
00575D56   32C0             XOR AL,AL


HOHO,575d37这里就是Magic Jump,重来强行跳即可转为运行解码进程.

第二步: OEP :
通过观察代码确定是Delphi的程序,寻找OEP,我们he GetModuleHandleA
看堆栈esp处,当发现:

引用:
0012FF94   004066D9  /CALL to GetModuleHandleA from mjcs.004066D4
0012FF98   00000000  \pModule = NULL


是从4066d9调用的时候,我们知道oep已经过了。
逐步返回

引用:

004C80DF   0055 8B          ADD BYTE PTR SS:[EBP-75],DL
004C80E2   EC               IN AL,DX                                 ; I/O command
004C80E3   83C4 F0          ADD ESP,-10
004C80E6   53               PUSH EBX
004C80E7   56               PUSH ESI
004C80E8   57               PUSH EDI
004C80E9   B8 387D4C00      MOV EAX,mjcs.004C7D38
004C80EE   E8 D5E5F3FF      CALL mjcs.004066C8
004C80F3   8B35 50FF4D00    MOV ESI,DWORD PTR DS:[4DFF50]            ; mjcs.004E0BF0   ****返回到这里
004C80F9   8B3D 80FD4D00    MOV EDI,DWORD PTR DS:[4DFD80]            ; mjcs.004E0BEC
004C80FF   B2 01            MOV DL,1
004C8101   A1 A8354200      MOV EAX,DWORD PTR DS:[4235A8]
004C8106   E8 11CCF5FF      CALL mjcs.00424D1C
004C810B   8BD8             MOV EBX,EAX
004C810D   BA 94824C00      MOV EDX,mjcs.004C8294                    ; ASCII "MS Sans Serif"
004C8112   8BC3             MOV EAX,EBX
004C8114   E8 FFCFF5FF      CALL mjcs.00425118
004C8119   BA 3C000000      MOV EDX,3C



往上看,看到了add esp,-10,HOHO,可以确定4c80e0就是OEP了
Dump吧!

第三步: IAT处理
ollydbg重新载入,转成本进程运行,he 4c80e0 到了oep后,Imprec上场。
IAT检测范围为: RVA  e218c Size 7B8
载入一看基本都识别不了,看来IAT做了处理。
到4e218c看看去!

引用:

004E218C  00 00 00 00 A0 4D 14 00 C0 4D 14 00 E0 4D 14 00  ...._M.繫.郙.
004E219C  00 4E 14 00 20 4E 14 00 40 4E 14 00 60 4E 14 00  .N. N.@N.`N.
004E21AC  80 4E 14 00 42 B9 57 00 A0 4E 14 00 C0 4E 14 00  N.B筗._N.繬.
004E21BC  E0 4E 14 00 00 4F 14 00 20 4F 14 00 40 4F 14 00  郚..O. O.@O.
004E21CC  60 4F 14 00 80 4F 14 00 A0 4F 14 00 C0 4F 14 00  `O.O._O.繭.
004E21DC  3A BA 57 00 E0 4F 14 00 00 50 14 00 20 50 14 00  :篧.郞..P. P.
004E21EC  40 50 14 00 60 50 14 00 29 BA 57 00 80 50 14 00  @P.`P.)篧.P.
004E21FC  A0 50 14 00 C0 50 14 00 E0 50 14 00 00 51 14 00  _P.繮.郟..Q.
004E220C  20 51 14 00 40 51 14 00 60 51 14 00 80 51 14 00   Q.@Q.`Q.Q.
004E221C  A0 51 14 00 C0 51 14 00 E0 51 14 00 00 52 14 00  _Q.繯.郠..R.
004E222C  20 52 14 00 40 52 14 00 60 52 14 00 80 52 14 00   R.@R.`R.R.
004E223C  A0 52 14 00 00 00 00 00 C0 52 14 00 E0 52 14 00  _R.....繰.郣.


通过观察,基本被指向了动态申请的内存区域 14xxxx
例如,结构如下:
00406604  -FF25 84224E00    JMP DWORD PTR DS:[4E2284]
=>  4e2284里的值为  145400
145400处代码为:
00145400   74 0E            JE SHORT 00145410                        ; JMP to kernel32.GetModuleHandleA
00145402   75 0C            JNZ SHORT 00145410                       ; JMP to kernel32.GetModuleHandleA
00145404   E8 1A32534E      CALL 4E678623
00145409   A6               CMPS BYTE PTR DS:[ESI],BYTE PTR ES:[EDI]
0014540A   40               INC EAX
0014540B   009A A54000FF    ADD BYTE PTR DS:[EDX+FF0040A5],BL
00145411   25 16541400      AND EAX,145416
00145416   29B5 807C0000    SUB DWORD PTR SS:[EBP+7C80],ESI
0014541C   0000             ADD BYTE PTR DS:[EAX],AL
0014541E   0000             ADD BYTE PTR DS:[EAX],AL
做了变形,所以得写代码修复。
找个区域写代码,我到140000处:

引用:

00140000   60               PUSHAD
00140001   9C               PUSHFD
00140002   BF 8C214E00      MOV EDI,4E218C
00140007   66:837F 02 14    CMP WORD PTR DS:[EDI+2],14
0014000C   75 22            JNZ SHORT 00140030
0014000E   8B17             MOV EDX,DWORD PTR DS:[EDI]
00140010   90               NOP
00140011   90               NOP
00140012   90               NOP
00140013   8B42 12          MOV EAX,DWORD PTR DS:[EDX+12]
00140016   8B18             MOV EBX,DWORD PTR DS:[EAX]
00140018   891F             MOV DWORD PTR DS:[EDI],EBX
0014001A   90               NOP
0014001B   EB 13            JMP SHORT 00140030
0014001D   90               NOP
0014001E   90               NOP
0014001F   90               NOP
00140020   90               NOP
00140021   90               NOP
00140022   90               NOP
00140023   90               NOP
00140024   90               NOP
00140025   90               NOP
00140026   90               NOP
00140027   90               NOP
00140028   90               NOP
00140029   90               NOP
0014002A   90               NOP
0014002B   90               NOP
0014002C   90               NOP
0014002D   90               NOP
0014002E   90               NOP
0014002F   90               NOP
00140030   83C7 04          ADD EDI,4
00140033   81FF 00304E00    CMP EDI,4E3000
00140039   7D 02            JGE SHORT 0014003D
0014003B  ^EB CA            JMP SHORT 00140007
0014003D   9D               POPFD
0014003E   61               POPAD

二进制代码:
60 9C BF 8C 21 4E 00 66 83 7F 02 14 75 22 8B 17 90 90 90 8B 42 12 8B 18 89 1F 90 EB 13 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 83 C7 04 81 FF 00 30 4E 00 7D 02 EB CA 9D 61



140000处新建EIP,在 14003e popad处设断点运行。
运行后,用imprec载入IAT,发现还有8个是坏的。

这8个壳做了特殊处理,因此我也来特殊处理 :)
找个delphi的程序把iat表保存下来和它对照一下,正确纠正这8个:
e21b0  57b942     GetTickCount
e21dc  57ba3a     GetStartupInfoA
e21f4 57ba29       GetCommandLineA
e22dc 57bca8     SearchPathA
e2328 57b9ec    GetVersionExA
e232c 57b942    GetVersion
e237c 57ba5a    GetCurrentProcessId
e2380 57bba0   GetCurrentDirectoryA

第四步: 完工,优化:
这里可以手工把dump.exe文件里的壳section代码给去掉,然后根据offset删除垃圾代码,修理好以后就可以Imprec Fix IAT了.
好了,测试运行正常.

我写文章目的,是让大家一起探讨解壳思路,还请各路大侠指教几番.