无驱动的 StarForce 输入表初探
最近股市不爽,多少看了一点,马上要高考了。又有多少学子在和命运赛跑。人生就是赛跑。
没有驱动,在ollydbg下基本没有什么阻力,很顺利的跑起来了。
发现壳把 GetModuleHandleA 函数抽走了,以下是函数原代码:

代码:
7C80B6A1 >  8BFF            mov     edi, edi
7C80B6A3    55              push    ebp
7C80B6A4    8BEC            mov     ebp, esp
7C80B6A6    837D 08 00      cmp     dword ptr [ebp+8], 0
7C80B6AA    74 18           je      short 7C80B6C4
7C80B6AC    FF75 08         push    dword ptr [ebp+8]
7C80B6AF    E8 C0290000     call    7C80E074
7C80B6B4    85C0            test    eax, eax
7C80B6B6    74 08           je      short 7C80B6C0
7C80B6B8    FF70 04         push    dword ptr [eax+4]
7C80B6BB    E8 7D2D0000     call    GetModuleHandleW
7C80B6C0    5D              pop     ebp
7C80B6C1    C2 0400         retn    4

还好,壳只是简单的模拟了一下这个函数:在函数GetModuleHandleW下断,返回后,可以看见一部分:
代码:
00C5017C    E8 00000000     call    00C50181
00C50181    870424          xchg    dword ptr [esp], eax
00C50184    8D40 0F         lea     eax, dword ptr [eax+F]
00C50187    870424          xchg    dword ptr [esp], eax
00C5018A    68 3DE4807C     push    kernel32.GetModuleHandleW
00C5018F    C3              retn
00C50190    5D              pop     ebp                              ; 00C00050
00C50191    E9 00000000     jmp     00C50196
00C50196    C2 0400         retn    4

下面还有抽走的另一个函数:
代码:
00C50199    64:A1 18000000  mov     eax, dword ptr fs:[18]
00C5019F    8B40 30         mov     eax, dword ptr [eax+30]
00C501A2    8B40 08         mov     eax, dword ptr [eax+8]
00C501A5  ^ E9 E6FFFFFF     jmp     00C50190

这个函数眼熟吧,嘿嘿。
又简单的看了一下iat的处理,真有点变态,跳来跳去,不过有一个非常大的漏洞可以利用:
代码:
100BA1B7    8B00            mov     eax, dword ptr [eax]
100BA1B9    8B0A            mov     ecx, dword ptr [edx]
100BA1BB    8908            mov     dword ptr [eax], ecx

上面的代码是写入iat的地方,这里同样会把正确的函数写入到壳申请的一段数据里。在这里下断,可以看见,壳写入程序的函数名称是最近写入壳里的函数名称,这个没有加密:
代码:
0040E318  76B2A8F7  WINMM.PlaySoundA

上面是写入的函数,下面是写入壳里的函数,最下面也就是最后写入的就是正确的函数:
代码:
00C70470  76B13894  WINMM.CloseDriver
00C70474  76B1E382  WINMM.DefDriverProc
00C70478  76B154A9  WINMM.DriverCallback
00C7047C  76B12DE5  WINMM.GetDriverModuleHandle
00C70480  76B12DE5  WINMM.GetDriverModuleHandle
00C70484  76B2B7B4  WINMM.winmmDbgOut
00C70488  76B1AAA0  WINMM.MigrateSoundEvents
00C7048C  76B20906  WINMM.NotifyCallbackData
00C70490  76B12E3E  WINMM.OpenDriver
00C70494  76B2A8F7  WINMM.PlaySoundA
00C70498  76B2A8F7  WINMM.PlaySoundA

还有加密的函数:
代码:
0040E25C  00CE017C

此时的数据如下:
代码:
00C70D50  7C874631  kernel32.GetLargestConsoleWindowSize
00C70D54  7C80902D  ASCII "NTDLL.RtlGetLastWin32Error"

看一下加密后函数地址处的代码:
代码:
00CE017C    64:A1 18000000  mov     eax, dword ptr fs:[18]
00CE0182    8B40 34         mov     eax, dword ptr [eax+34]
00CE0185    C3              retn

再看一下NTDLL.RtlGetLastWin32Error函数的代码:
代码:
7C930331 >  64:A1 18000000  mov     eax, dword ptr fs:[18]
7C930337    8B40 34         mov     eax, dword ptr [eax+34]
7C93033A    C3              retn

上面00C70D54处的NTDLL.RtlGetLastWin32Error就是加密的函数名称,当然还需要获得这个函数的实际地址,然后填充到程序中去。
不过并不完全都是这样,有的就是直接的函数地址,比如:
代码:
0040E1E0  00CC01B5

壳中函数是:
代码:
00C70EF4  7C862C9C  kernel32.GetThreadTimes
00C70EF8  7C80929C  kernel32.GetTickCount

看一下00CC01B5处的代码:
代码:
00CC01B5  ^\E9 C2FFFFFF     jmp     00CC017C

跟随这个跳转:
代码:
00CC017C    BA 0000FE7F     mov     edx, 7FFE0000
00CC0181    FF32            push    dword ptr [edx]
00CC0183    58              pop     eax
00CC0184    F762 04         mul     dword ptr [edx+4]
00CC0187    0FACD0 18       shrd    eax, edx, 18
00CC018B    E9 00000000     jmp     00CC0190
00CC0190    C3              retn

看一下kernel32.GetTickCount的代码:
代码:
7C80929C >  BA 0000FE7F     mov     edx, 7FFE0000
7C8092A1    8B02            mov     eax, dword ptr [edx]
7C8092A3    F762 04         mul     dword ptr [edx+4]
7C8092A6    0FACD0 18       shrd    eax, edx, 18
7C8092AA    C3              retn

这个就可以直接填充到程序中了。
没有虚拟机,只有输入表加密,没有驱动的脱壳生活里真是太舒服了。如果找到这个加密代码的关键地方也可以直接改代码,使其写入正确的函数,不过这段代码实在是太长太乱了。壳应该是用自己的dll实现解码,不知道直接搞dll能不能实现脱壳。
要是研究一下壳还原代码的算法,是不是可以直接写出脱壳机来呢?太复杂了,还是不要考虑的好。