作为初学者,成功脱掉这个壳,并不容易,记录下来给自己也给比我还新的兄弟一个纪念,有什么不对的地方请各位大侠指正。
软件名称:国外软件,不便透露
目标:脱掉Aspr 2.1X SKE壳
一、初探目标
这个软件比较大,安装好后,有30多兆。进入安装目录,用PeiD查看里面的文件,两个exe文件被加壳,一个dll文件被加壳,其它的文件未加
壳,PeiD探测都为VC5或VC6编写。
二、三个文件加壳参数是否一样?
将两个exe文件分别称为1.exe和2.exe,dll文件称为3.dll。
1、先看看OEP
三个文件找OEP的方法不一定相同,两个exe文件用syscom提供的特征码方法很管用,即找到最后一次异常前面的一个函数进入运行到返回,出
来即是OEP了;至于dll文件用这个方法好象行不通,我用的是最后一次异常后对code段下断点的方法,直接到OEP了。
--1.exe
OD去除忽略内存异常载入,Shift+F9,发现最后一次异常为32~36次不等:
011802F3 55 push ebp ; XXXXX.0044C07A
011802F4 EB 02 jmp short 011802F8
011802F6 CD20 1BEA8BEC vxdjump EC8BEA1B
011802FC 6A FF push -1
011802FE 50 push eax
011802FF 66:9C pushfw
01180301 53 push ebx
01180302 81DB E8A90959 sbb ebx,5909A9E8
01180308 81F3 4060C549 xor ebx,49C56040
0118030E 8D5C24 1D lea ebx,dword ptr ss:[esp+1D]
01180312 EB 01 jmp short 01180315
01180314 E8 8D5C2BE3 call E4435FA6
01180319 EB 02 jmp short 0118031D
0118031B CD20 2BDD8D5B vxdjump 5B8DDD2B
01180321 06 push es
01180322 36:EB 01 jmp short 01180326
01180325 9A 68003A4E 008>call far 8F00:4E3A0068
0118032C 035B 66 add ebx,dword ptr ds:[ebx+66]
0118032F 9D popfd
01180330 E9 6C010000 jmp 011804A1
01180335 68 7C041801 push 118047C
0118033A E8 C1FC0500 call 011E0000
0118033F 56 push esi
01180340 66:9C pushfw
明显OEP被抽了,按照上面的布局,OEP应该为下面才对:
push ebp
mov ebp,esp
push -1
push XXXXXXXX
push XXXXXXXX
先不管,看2.exe
--2.exe
00FB025F 55 push ebp
00FB0260 EB 02 jmp short 00FB0264
00FB0262 CD20 81DD9786 vxdjump 8697DD81
00FB0268 64:7A 83 jpe short 00FB01EE
00FB026B ED in eax,dx
00FB026C 0F036C24 18 lsl ebp,dword ptr ss:[esp+18]
00FB0271 8D6C24 29 lea ebp,dword ptr ss:[esp+29]
00FB0275 8D6C25 D7 lea ebp,dword ptr ss:[ebp-29]
00FB0279 6A FF push -1
00FB027B 50 push eax
00FB027C 66:9C pushfw
00FB027E 53 push ebx
00FB027F 8D5F BD lea ebx,dword ptr ds:[edi-43]
00FB0282 8D5C11 67 lea ebx,dword ptr ds:[ecx+edx+67]
00FB0286 2BDA sub ebx,edx
00FB0288 EB 02 jmp short 00FB028C
00FB028A CD20 8D5C2450 vxdcall 50245C8D
00FB0290 8D5C2B B0 lea ebx,dword ptr ds:[ebx+ebp-50]
基本同1.exe,OEP也被抽了;SEH异常次数也为32到36次,由于后面经常要去OEP,自己写了段osc(ollyscript也是刚学的,大家不要笑),因为
不知道确切的SEH异常次数(每次基本都会变),可能会报错,7成机会能找到。。。
/*
ASProtect SKE 2.1X直达OEP脚本By LiD
*/
var i//循环因子
var j//临时变量
var k//特征码找到标志位
mov i, 0//赋初值
mov k, 0
SEH_LOOP:
cmp i, 20//这个值可酌情修改,此表32次异常后开始搜索特征字符串
jae FIND_OP
esto
inc i
jmp SEH_LOOP
FIND_OP:
mov j, eip
shr j, 10
shl j, 10
find j, #83C42C5D5F5E5BC3#//特征码
mov j, $RESULT
cmp $RESULT, 0
je GO_ON_LOOP
sub j, 5
bp j
mov k, 1
jmp GO_ON_LOOP
GO_ON_LOOP:
cmp k, 0
je TMP_FIND_OP1
jmp TMP_FIND_OP2
TMP_FIND_OP1:
esto
jmp FIND_OP
TMP_FIND_OP2:
esto
eval "{j}"
msg $RESULT
cmp j, eip
je FIND_OK
jmp TMP_FIND_OP2
FIND_OK:
bc j
sti
rtr
sto
--3.dll
10007221 55 push ebp
10007222 8BEC mov ebp,esp
10007224 53 push ebx
10007225 8B5D 08 mov ebx,dword ptr ss:[ebp+8]
10007228 56 push esi
10007229 8B75 0C mov esi,dword ptr ss:[ebp+C]
1000722C 57 push edi
1000722D 8B7D 10 mov edi,dword ptr ss:[ebp+10]
10007230 85F6 test esi,esi
10007232 75 09 jnz short XXXXXXXX3.1000723D
10007234 833D 24B40010 0>cmp dword ptr ds:[1000B424],0
1000723B EB 26 jmp short XXXXXXXX3.10007263
用loaddll载入,SEH次数也为32到36次左右;
dll文件的OEP好象没被抽,庆幸!
2、代码段的函数调用是否被处理
到OEP后,进入代码段,发现有如下代码:
004075F1 /E9 A0000000 jmp XXXXXXXX1.00407696
004075F6 |8B4D F8 mov ecx,dword ptr ss:[ebp-8]
004075F9 |C741 48 0000000>mov dword ptr ds:[ecx+48],0
00407600 |8B55 F8 mov edx,dword ptr ss:[ebp-8]
00407603 |C782 EC030000 0>mov dword ptr ds:[edx+3EC],0
0040760D |6A 20 push 20
0040760F |E8 EC89D900 call 011A0000<----------------------------注意这里,又进壳
00407614 |79 50 jns short XXXXXXXX1.00407666
00407616 |E8 E589D900 call 011A0000<----------------------------注意这里,又进壳
0040761B |2F das
0040761C |8B4D F8 mov ecx,dword ptr ss:[ebp-8]
0040761F |E8 05620000 call XXXXXXXX1.0040D829
00407624 |8B45 F8 mov eax,dword ptr ss:[ebp-8]
00407627 |8B88 0C040000 mov ecx,dword ptr ds:[eax+40C]
0040762D |51 push ecx
里面的call 011A0000明显表明原函数被壳处理过了。我们需要把这些改成call XXXXXX1.0040766或jmp XXXXXX1.0040766之类的才对;
同样载入2.exe并运行,发现如下代码:
004010AC FF15 3C504000 call dword ptr ds:[40503C] ; kernel32.SetEvent
004010B2 8B0D E47B4000 mov ecx,dword ptr ds:[407BE4]
004010B8 6A FF push -1
004010BA 51 push ecx
004010BB E8 44EFC100 call 01020004<----------------------------
004010C0 848B 15E47B40 test byte ptr ds:[ebx+407BE415],cl
004010C6 008B 35445040 add byte ptr ds:[ebx+40504435],cl
004010CC 0052 FF add byte ptr ds:[edx-1],dl
004010CF D6 salc
004010D0 A1 E07B4000 mov eax,dword ptr ds:[407BE0]
004010D5 50 push eax
004010D6 FFD6 call esi
。。。
。。。
00402DAC 68 B47B4000 push XXXXXXXX2.00407BB4
00402DB1 6A 00 push 0
00402DB3 8BF1 mov esi,ecx
00402DB5 6A 00 push 0
00402DB7 E8 48D2C700 call 01080004<-----------------------------
00402DBC 54 push esp
00402DBD A3 007E4000 mov dword ptr ds:[407E00],eax
00402DC2 FF15 28504000 call dword ptr ds:[405028] ; ntdll.RtlGetLastWin32Error
00402DC8 3D B7000000 cmp eax,0B7
00402DCD A3 047E4000 mov dword ptr ds:[407E04],eax
00402DD2 75 18 jnz short XXXXXXXX2.00402DEC
上面的call 01020004和call 01080004也表明原函数被壳处理过了。但注意,这个与1.exe的类型不一样,它每次call的函数不一样,1.exe是
一样的(call 011A0000);仔细想一想,其实2比1要简单,为什么?如果写过程序的应该都知道,如果你想完成同样一个功能,写一个函数要传
的参数要多一些,而分开写成几个函数要传的参数要少一些。可以跟进去比较一下。。。
--3.dll
这个函数没被处理,好办;
3、看看IAT表有没有加密
--1.exe
到OEP后随便选一个已知的函数调用,看call到哪儿的,就可以得到IAT表了(如上面2.exe里面的kernel32.SetEvent里面对应的地址是40503C
,则说明IAT表在这个地址前后,去看看就知道了,用IR填入OEP也可以自动找出来)
004D3000 77DBB635 ADVAPI32.ControlService
004D3004 77DAEDE5 ADVAPI32.RegDeleteValueA
004D3008 77DBB88C ADVAPI32.OpenServiceA
004D300C 77DCCAC3 ADVAPI32.RegEnumKeyA
004D3010 77DB5EB8 ADVAPI32.QueryServiceStatus
004D3014 77E06CC9 ADVAPI32.ChangeServiceConfigA
004D3018 77DC5462 ADVAPI32.QueryServiceConfigA
004D301C 77DC3238 ADVAPI32.StartServiceA
004D3020 77DAEAF4 ADVAPI32.RegCreateKeyExA
...
004D41C0 003D30C0 versit.addPropValue
004D41C4 003D2C10 versit.newVObject
004D41C8 003D2F50 versit.moreIteration
004D41CC 003D2D10 versit.vObjectVObjectValue
004D41D0 003D34A0 versit.cleanVObject
004D41D4 003D2CB0 versit.vObjectName
004D41D8 003D2C30 versit.deleteVObject
004D41DC 003D1550 versit.Parse_MIME
004D41E0 00000000
004D41E4 00000000
很明显,IAT未加密,4D3000~4D41E0,大小还用说,FT。
(提示:数据窗口选长型的地址会好看一些,因为后面会显示有哪些函数,加密没加密一眼就能看出来)
--2.exe
00405000 7C802530 kernel32.WaitForSingleObject
00405004 7C801EEE kernel32.GetStartupInfoA
00405008 7C80B529 kernel32.GetModuleHandleA
0040500C 7C8114AB kernel32.GetVersion
00405010 7C81CACB kernel32.TerminateThread
00405014 7C921005 ntdll.RtlEnterCriticalSection
00405018 7C9210ED ntdll.RtlLeaveCriticalSection
0040501C 7C80AA66 kernel32.FreeLibrary
00405020 7C93188A ntdll.RtlDeleteCriticalSection
00405024 7C80EB3F kernel32.CreateMutexA
...
004052D8 100017D0 XXXXXX.BT_RegisterCallback
004052DC 10001B70 XXXXXX.BT_GetDefaultAudioDeviceInfo
004052E0 10001D30 XXXXXX.BT_CheckFeature
004052E4 10001C10 XXXXXX.BT_SendProfileCommand
004052E8 00000000
004052EC 00000000
也未加密,405000~4052E8
--3.dll
10008000 77DA761B ADVAPI32.RegOpenKeyExA
10008004 558D75B5
10008008 78697766
1000800C 00000000
10008010 A9EDAAF5
10008014 7C8099BD kernel32.LocalAlloc
10008018 7C80995D kernel32.LocalFree
1000801C CA4E820F
10008020 84803644
10008024 7C8114AB kernel32.GetVersion
10008028 7C653F8E
1000802C 7C809B77 kernel32.CloseHandle
10008030 7C823053 kernel32.SetCurrentDirectoryA
10008034 9218F02F
10008038 7C80EC1B kernel32.OpenMutexA
1000803C 7C802442 kernel32.Sleep
10008040 00000000
10008044 73D98D67 mfc42.#4486
10008048 73D35EF1 mfc42.#2554
...
10008190 77E5A77B RPCRT4.NdrServerInitializeNew
10008194 77E81AFC RPCRT4.NdrFixedArrayMarshall
10008198 77E5887B RPCRT4.RpcBindingFree
1000819C 77E5B769 RPCRT4.NdrConformantArrayMarshall
100081A0 00000000
100081A4 00000000
这个就没那么幸运了,其中的没显示出函数名称的都是被加过密了,10008000~100081A0
三、开始修复
1、=====>针对3.dll先把IAT弄出来吧。。。
我采用的是林海雪原的方法,主要是比较简单:找解码IAT表出来之前的最后一次SEH(大概16到20次不等),下面的位置ASCII"85"是一个参考
点,找到两个ascII "85"之间正中一个函数:00949871,F7进去(先F2下断,再Shift+F9过来)
00949860 68 B8A09400 push 94A0B8 ; ASCII "85"
00949865 E8 B2B7FFFF call 0094501C
0094986A A1 10979500 mov eax,dword ptr ds:[959710]
0094986F 8B00 mov eax,dword ptr ds:[eax]
00949871 E8 8EDE0000 call 00957704<-------------------------------先断在这里,Shift+F9过来,再F7进去
00949876 84C0 test al,al
00949878 75 0A jnz short 00949884
0094987A 68 B8A09400 push 94A0B8 ; ASCII "85"
进去之后,往下找到这段:
00957834 E8 8BFCFFFF call 009574C4<-------------------------------从这儿进去,就是解码函数了(F4过来,再F7进去)
00957839 0FB707 movzx eax,word ptr ds:[edi]
0095783C 83C0 02 add eax,2
0095783F 03F8 add edi,eax
00957841 8A1F mov bl,byte ptr ds:[edi]
00957843 47 inc edi
00957844 3A5E 34 cmp bl,byte ptr ds:[esi+34]
00957847 ^ 75 8A jnz short 009577D3<<<<<<<<<<<<<<<内循环:换函数,如kernel32.OpenMutexA->kernel32.Sleep等
00957849 8BDF mov ebx,edi
0095784B 8B03 mov eax,dword ptr ds:[ebx]
0095784D 85C0 test eax,eax
0095784F ^ 0F85 20FFFFFF jnz 00957775<<<<<<<<<<<<<<<<<<<<<外循环:换模块,如kernel32->mfc42等
00957855 8A0424 mov al,byte ptr ss:[esp]
00957858 83C4 0C add esp,0C
0095785B 5D pop ebp<--------------------------------------这几句可以为一个参考
0095785C 5F pop edi
0095785D 5E pop esi
0095785E 5B pop ebx
0095785F C3 retn
00957860 C3 retn<-----------------------------------------
再进来,就是:
009574C4 55 push ebp
009574C5 8BEC mov ebp,esp
009574C7 81C4 F8FEFFFF add esp,-108
009574CD 53 push ebx
009574CE 56 push esi
009574CF 57 push edi
009574D0 8B55 14 mov edx,dword ptr ss:[ebp+14]
009574D3 8B5D 08 mov ebx,dword ptr ss:[ebp+8]
009574D6 8DBD FAFEFFFF lea edi,dword ptr ss:[ebp-106]
009574DC 8BC2 mov eax,edx
009574DE 48 dec eax
009574DF 83E8 02 sub eax,2
009574E2 0FB630 movzx esi,byte ptr ds:[eax]
009574E5 8B45 10 mov eax,dword ptr ss:[ebp+10]
009574E8 83E8 02 sub eax,2
009574EB 0FB600 movzx eax,byte ptr ds:[eax]
009574EE 3B43 2C cmp eax,dword ptr ds:[ebx+2C]
009574F1 76 06 jbe short 009574F9
009574F3 8943 2C mov dword ptr ds:[ebx+2C],eax
009574F6 EB 01 jmp short 009574F9
009574F8 6933 C08A433B imul esi,dword ptr ds:[ebx],3B438AC0
009574FE 3BF0 cmp esi,eax<---------------------------------------这儿就是传说中的四种类型比较了
00957500 75 5E jnz short 00957560
00957502 EB 01 jmp short 00957505
用林海兄的方法,先分配一块空间,把009574Fe处的cmp esi,eax改成jmp 分配的空间(注意,因为是long jmp,所以这个字节(5B)比cmp
esi,eax(3B)占的要多,就把下面的两行给占用了,要先保存下来)
代码无非就是把esi里面的两个值改成另外两个,不知道哪个改哪个,多试两次看下方的IAT表变化就知道了,这儿不详述。我的只有三个值,
只改一个值,得如下修正后的IAT,先二进制复制下来:
10008000 77DA761B ADVAPI32.RegOpenKeyExA
10008004 77DA7883 ADVAPI32.RegQueryValueExA
10008008 77DA6BF0 ADVAPI32.RegCloseKey
1000800C 00000000
10008010 7C8397A1 kernel32.GetCurrentDirectoryA
10008014 7C8099BD kernel32.LocalAlloc
10008018 7C80995D kernel32.LocalFree
1000801C 7C802530 kernel32.WaitForSingleObject
10008020 7C81CACB kernel32.TerminateThread
10008024 7C8114AB kernel32.GetVersion
10008028 7C81082F kernel32.CreateThread
1000802C 7C809B77 kernel32.CloseHandle
10008030 7C823053 kernel32.SetCurrentDirectoryA
10008034 7C86114D kernel32.WinExec
10008038 7C80EC1B kernel32.OpenMutexA
1000803C 7C802442 kernel32.Sleep
10008040 00000000
10008044 73D98D67 mfc42.#4486
...
当然,这点也可以在OEP后,调IR,选择loaddll.exe进程,再选取3.dll进程,不要填OEP,手动填RVA:8000,Size:16C,获取输入表,手工修
复这些函数(如果你知道这些函数是什么的话,因为我不知道,呵呵。。。)
到这里,3.dll文件可以先dump为3-1.dll了--(为安全起见,先ctrl+F2重新来过,到OEP处,将IAT表帖回去,再dump)
2、到这儿,以上3个文件不能同时处理了,要花分两头,各表一支了:
--1.exe与2.exe因为code段函数调用加密,还要再处理
--而3.dll由于是dll文件,需要找重定位表的位置和大小
--先找3.dll文件的重定位表
ctrl+F2重新来过,大概在10多次(我这儿16次)异常后,找到这儿,可以用原来1.23RC里面的命令序列(精华6,Fly大侠的文章)搜索:
0094A32B /EB 3B jmp short 0094A368
0094A32D |8B06 mov eax,dword ptr ds:[esi]
0094A32F |8B00 mov eax,dword ptr ds:[eax]
0094A331 |03C7 add eax,edi
0094A333 |8B5424 0C mov edx,dword ptr ss:[esp+C]
0094A337 |E8 D873FFFF call 00941714<------------------------------到这儿下断,ebx值就是重定位表的RVA
0094A33C |8BE8 mov ebp,eax
0094A33E |892D 3CB49500 mov dword ptr ds:[95B43C],ebp
0094A344 |3B6C24 10 cmp ebp,dword ptr ss:[esp+10]
0094A348 |74 0C je short 0094A356
0094A34A |68 DCA39400 push 94A3DC ; ASCII "45
"
0094A34F |E8 C8ACFFFF call 0094501C
0094A354 |EB 12 jmp short 0094A368
0094A356 |8B4424 0C mov eax,dword ptr ss:[esp+C]
0094A35A |A3 38B49500 mov dword ptr ds:[95B438],eax
0094A35F |8B4424 08 mov eax,dword ptr ss:[esp+8]
0094A363 |A3 34B49500 mov dword ptr ds:[95B434],eax
0094A368 \833C24 00 cmp dword ptr ss:[esp],0
0094A36C 74 5D je short 0094A3CB<-------------------------这儿下断,将Z位由1改为0,让它不要跳
0094A36E 035C24 04 add ebx,dword ptr ss:[esp+4]
0094A372 EB 51 jmp short 0094A3C5
0094A374 8D43 04 lea eax,dword ptr ds:[ebx+4]
0094A377 8B00 mov eax,dword ptr ds:[eax]
0094A379 83E8 08 sub eax,8
0094A37C D1E8 shr eax,1
0094A37E 8BFA mov edi,edx
0094A380 037C24 04 add edi,dword ptr ss:[esp+4]
0094A384 83C3 08 add ebx,8
0094A387 8BF0 mov esi,eax
0094A389 85F6 test esi,esi
0094A38B 76 38 jbe short 0094A3C5
0094A38D 66:8B13 mov dx,word ptr ds:[ebx]
0094A390 0FB7C2 movzx eax,dx
0094A393 C1E8 0C shr eax,0C
0094A396 66:83E8 01 sub ax,1
0094A39A 72 23 jb short 0094A3BF
0094A39C 66:83E8 02 sub ax,2
0094A3A0 74 02 je short 0094A3A4
0094A3A2 EB 11 jmp short 0094A3B5
0094A3A4 66:81E2 FF0F and dx,0FFF
0094A3A9 0FB7C2 movzx eax,dx
0094A3AC 03C7 add eax,edi
0094A3AE 8B1424 mov edx,dword ptr ss:[esp]
0094A3B1 0110 add dword ptr ds:[eax],edx
0094A3B3 EB 0A jmp short 0094A3BF
0094A3B5 68 ECA39400 push 94A3EC ; ASCII "34
"
0094A3BA E8 5DACFFFF call 0094501C
0094A3BF 83C3 02 add ebx,2
0094A3C2 4E dec esi
0094A3C3 ^ 75 C8 jnz short 0094A38D
0094A3C5 8B13 mov edx,dword ptr ds:[ebx]
0094A3C7 85D2 test edx,edx
0094A3C9 ^ 75 A9 jnz short 0094A374
0094A3CB 83C4 14 add esp,14
0094A3CE 5D pop ebp<--------------------------------------F4到这儿,再看ebx值,bx减去上面的bx就是size了撒
0094A3CF 5F pop edi
0094A3D0 5E pop esi
0094A3D1 5B pop ebx
0094A3D2 C3 retn
最后得:
RVA:D000 Size:86C
用loadpe编辑3-1.dll文件,填入新的重定位表地址和大小(在Directories里面),重建PE,OK了。。。呵呵
用Peid查,再也不是讨厌的的ASProtect 2.1x SKE -> Alexey Solodovnikov了,而是:
而是Microsoft Visual C++ 6.0 DLL了,呵呵。用loaddll运行OK,用主程序运行,好象有些问题,估计是主程序里面有检测dll文件size之类
的,爆破的问题了。
--再表exe文件,exe文件这步主要处理code段里面调用到壳里的函数或jmp
--1.exe
call 011A0000,就这一个,总体思路参照shoooo的"nspack3.5主程序脱壳分析",具体操作参考kanxue老大的"Asprotect SKE 2.2 的Advanced
Import protection保护技术",程序我是用的kanxue老大的现成的程序,先记录处理了哪些函数,后面再修复。代码我就不写了,看kanxue老
大的文章,我的是里面说的a情况,出来之后的类型全部是FF15,即call *,没有jmp *的情况(我就说原来用搜索改jmp怎么不行。。)。
分两步进行的,1、patch1先记录哪些函数处理了;2、再次运行,到OEP后,再调用patch2把这些call 011A0000替换过来,
完成后,dump为1-1.exe
--2.exe
先在OEP处dump为2-1.exe。
对于call 01020004;call 01040004;...的情况,我搜索了一下,一共有12处,在wak没放文章之前,我是手工修复的,过程很简单,跟踪原程序的
这些函数进去一个一个找出来。这种比较简单,如:
004010AC FF15 3C504000 call dword ptr ds:[40503C] ; kernel32.SetEvent
004010B2 8B0D E47B4000 mov ecx,dword ptr ds:[407BE4]
004010B8 6A FF push -1
004010BA 51 push ecx
004010BB E8 44EFC100 call 01020004<--------------------------------回车进去
004010C0 848B 15E47B40 test byte ptr ds:[ebx+407BE415],cl
004010C6 008B 35445040 add byte ptr ds:[ebx+40504435],cl
004010CC 0052 FF add byte ptr ds:[edx-1],dl
004010CF D6 salc
004010D0 A1 E07B4000 mov eax,dword ptr ds:[407BE0] 01020004 F2: prefix repne:
01020005 EB 01 jmp short 01020008<---------------------------回车进去
01020007 - E9 FF0424E9 jmp EA26050B
0102000C F0:FFFE ??? ; 未知命令
0102000F FF00 inc dword ptr ds:[eax]
01020011 0000 add byte ptr ds:[eax],al
01020013 0000 add byte ptr ds:[eax],al 01020008 FF0424 inc dword ptr ss:[esp]
0102000B - E9 F0FFFEFF jmp 01010000<---------------------------------回车进去
01020010 0000 add byte ptr ds:[eax],al
01020012 0000 add byte ptr ds:[eax],al
01020014 0000 add byte ptr ds:[eax],al 01010000 8BFF mov edi,edi
01010002 55 push ebp
01010003 8BEC mov ebp,esp
01010005 6A 00 push 0
01010007 FF75 0C push dword ptr ss:[ebp+C]
0101000A FF75 08 push dword ptr ss:[ebp+8]
0101000D 68 4225807C push 7C802542<--------------------------------ctrl+g到这个地址去
01010012 68 5025807C push kernel32.WaitForSingleObjectEx
01010017 C3 retn 7C802530 > 8BFF mov edi,edi
7C802532 55 push ebp
7C802533 8BEC mov ebp,esp
7C802535 6A 00 push 0
7C802537 FF75 0C push dword ptr ss:[ebp+C]
7C80253A FF75 08 push dword ptr ss:[ebp+8]
7C80253D E8 0E000000 call kernel32.WaitForSingleObjectEx
7C802542 5D pop ebp<--------------------------------------到这儿
7C802543 C2 0800 retn 8
同上面比较,它把7C802530这个函数的前面几句给偷了,在IAT中查找7C802530,为:
00405000 7C802530 kernel32.WaitForSingleObject
将2-1.exe中本call改为call dword ptr ds:[405000]//注意:这个为6个字节,原来为5个字节,改的时候不要选择填充nop
依次改完所有的这种call,aspr处理这理很怪,全是01020004,01040004,01060004...,01160004这样的,居然有规律?
修改完后,保存。
*****后面wak放了一篇文章,可以用程序把这些函数记录下来,我试了一下,文章1里面的函数是没问题的,文章2里面的函数找不到地方,郁
闷。这里理论上应该也可以用kanxue的方法记录这些函数,我没试过,不知道灵不灵。
四、修复OEP处的stolen code
--只对1.exe和2.exe有用,两个基本一样,就只表一个了
我试过将它所需要的区段全部帖上去的方法,但区段太多,根本就帖不上去,loadpe友情提示我,“区段太多了,不能再帖。。。”
大侠说这是体力活,其实不单是体力活,也是脑力活,要去排除aspr的垃圾代码也很难啊。。。
1.exe OEP:
011802F3 55 push ebp<-------------------------------------OEP
011802F4 EB 02 jmp short 011802F8
011802F6 CD20 1BEA8BEC vxdjump EC8BEA1B
011802FC 6A FF push -1
011802FE 50 push eax
011802FF 66:9C pushfw
01180301 53 push ebx
01180302 81DB E8A90959 sbb ebx,5909A9E8
01180308 81F3 4060C549 xor ebx,49C56040
0118030E 8D5C24 1D lea ebx,dword ptr ss:[esp+1D] ctrl+g输入00401000到代码段,ctrl+f搜索jmp 011802F3,找到原OEP,修复之旅就从这儿开始。
004B0014 - E9 DA02CD00 jmp 011802F3<---------------------------------原OEP
004B0019 D7 xlat byte ptr ds:[ebx+al]---------------------这些都是垃圾
004B001A 25 CFD1DD87 and eax,87DDD1CF。。。。。。。。。。。。。。。。。
004B001F 8C0F mov word ptr ds:[edi],cs。。。。。。。。。。。。
004B0021 2A75 E2 sub dh,byte ptr ss:[ebp-1E]。。。。。。。。。
先用IR修复dump出的1-1.exe,填OEP为004B0014,转存为1-1_.exe
用OD载入1-1_.exe到004B0014处,另外OD再载入原程序1.exe到011802F3处,两边同时进行:
再1-1_.exe里面填1.exe里面的有用代码。。。主要是去除垃圾代码。下面是去除垃圾后的部分OEP
004B0014 > 55 push ebp
004B0015 8BEC mov ebp,esp
004B0017 6A FF push -1
004B0019 68 003A4E00 push XXXXXXXX.004E3A00
004B001E 68 C2FD4A00 push <jmp.&msvcrt._except_handler3>
004B0023 64:A1 00000000 mov eax,dword ptr fs:[0]
004B0029 50 push eax
004B002A 64:8925 0000000>mov dword ptr fs:[0],esp
...
我发现我这边程序中垃圾代码主要有这几个地方,
1)、胡乱jmp
象OEP处,才push ebp就jmp了,这种情况,把代码去除jmp后复制到记事本再分析
2)、代码变形
如push 404134
它会这样处理:
00C20785 66:9C pushfw
00C20787 57 push edi
00C20788 13FB adc edi,ebx
00C2078A F2: prefix repne:
00C2078B EB 01 jmp short 00C2078E
00C2078E 8D7C51 07 lea edi,dword ptr ds:[ecx+edx*2+7]//N
00C20792 8D7C24 12 lea edi,dword ptr ss:[esp+12]//
00C20796 8D7F EE lea edi,dword ptr ds:[edi-12]//
00C20799 8D7F 06 lea edi,dword ptr ds:[edi+6]//edi = esp + 6
00C2079C 56 push esi
00C2079D 64:EB 02 jmp short 00C207A2
00C207A2 81CE CEE6ACC5 or esi,C5ACE6CE//N
00C207A8 1BF5 sbb esi,ebp//N
00C207AA 8DB40B 34414000 lea esi,dword ptr ds:[ebx+ecx+404134]//
00C207B1 2BF1 sub esi,ecx//
00C207B3 2BF3 sub esi,ebx//esi = 00404134
00C207B5 56 push esi
00C207B6 F2: prefix repne:
00C207B7 EB 01 jmp short 00C207BA
00C207BA 8F07 pop dword ptr ds:[edi] ; XXXXXX.00404134
00C207BC 5E pop esi
00C207BD 5F pop edi
00C207BE 66:9D popfw
如果是这种情况,在pushfw和popfw的时候,多看看堆栈,就明白了,实际它只是压入了一个值,没进行其它操作,当然,为安全起见,还是一
行一行的跟最好。关于代码变形,kanxue的文章里面有很多。
3)、函数/跳转的处理
两种情形会call 011E0000,都需要跟进去,不过还好,都是用的一个函数在处理,如:
01180330 /E9 6C010000 jmp 011804A1
01180335 |68 7C041801 push 118047C<-----------------------------------------------------------------这是调用后的返回点
0118033A |E8 C1FC0500 call 011E0000<----------------------------------------------------------------F7进去
//还原回来应该是call 系统函数或自定义的函数,取决于第三层的返回值
或跳转
01180492 3AC3 cmp al,bl
01180494 0F84 4D010000 je 011805E7
0118049A 3C 20 cmp al,20
0118049C E8 5FFB0500 call 011E0000<----------------------------------------------------------------F7进去
//还原回来应该是jnz/jz之类的,后面地址为第三层的返回值
跟进到这儿,第一层
011E0000 /EB 01 jmp short 011E0003
011E0002 |9A 569CBEB6 AE4>call far 40AE:B6BE9C56
011E0009 0003 add byte ptr ds:[ebx],al
011E000B 74 24 je short 011E0031
011E000D 3883 EC2081F6 cmp byte ptr ds:[ebx+F68120EC],al
011E0013 229A 86FE81DE and bl,byte ptr ds:[edx+DE81FE86]
011E0019 3A45 EC cmp al,byte ptr ss:[ebp-14]
011E001C B8 EB019A8D mov eax,8D9A01EB
011E0021 74 24 je short 011E0047
011E0023 46 inc esi
011E0024 8D76 BA lea esi,dword ptr ds:[esi-46]
011E0027 895E 0C mov dword ptr ds:[esi+C],ebx
011E002A BB C20D4B00 mov ebx,4B0DC2
011E002F 335C24 28 xor ebx,dword ptr ss:[esp+28]
011E0033 51 push ecx
011E0034 8F46 04 pop dword ptr ds:[esi+4]
011E0037 8D0C0B lea ecx,dword ptr ds:[ebx+ecx]
011E003A EB 01 jmp short 011E003D
011E003C - 0F89 560883CA jns CBA10898
011E0042 1BF2 sbb esi,edx
011E0044 EB 01 jmp short 011E0047
011E0046 F0:C1C2 3D lock rol edx,3D ; 不允许锁定前缀
011E004A 57 push edi
011E004B 8F46 1C pop dword ptr ds:[esi+1C]
011E004E 26:EB 02 jmp short 011E0053
011E0051 CD20 0BF98946 vxdjump 4689F90B
011E0057 0083 E8693344 add byte ptr ds:[ebx+443369E8],al
011E005D 24 28 and al,28
011E005F 55 push ebp
011E0060 8F46 14 pop dword ptr ds:[esi+14]
011E0063 13EB adc ebp,ebx
011E0065 0BDD or ebx,ebp
011E0067 8BDC mov ebx,esp
011E0069 8D5C3B 2C lea ebx,dword ptr ds:[ebx+edi+2C]
011E006D 2BDF sub ebx,edi
011E006F 53 push ebx
011E0070 EB 02 jmp short 011E0074
011E0072 CD20 81EB454C vxdjump 4C45EB81
011E0078 CD E8 int 0E8
011E007A 56 push esi
011E007B 23D9 and ebx,ecx
011E007D 65:EB 01 jmp short 011E0081
011E0080 69BB 6ABD4400 C>imul edi,dword ptr ds:[ebx+44BD6A],8DF1C3C1
011E008A 5E pop esi
011E008B 2326 and esp,dword ptr ds:[esi]
011E008D EB 02 jmp short 011E0091
011E008F CD20 8D5C23DD vxdcall DD235C8D
011E0095 83C3 20 add ebx,20
011E0098 8B1B mov ebx,dword ptr ds:[ebx]
011E009A 53 push ebx
011E009B 8D9C11 F03B4600 lea ebx,dword ptr ds:[ecx+edx+463BF0]
011E00A2 335C24 28 xor ebx,dword ptr ss:[esp+28]
011E00A6 C1DB D1 rcr ebx,0D1
011E00A9 EB 01 jmp short 011E00AC
011E00AB 9A 035C2438 F2E>call far EBF2:38245C03
011E00B2 01F0 add eax,esi
011E00B4 BB 827A4400 mov ebx,447A82
011E00B9 F3: prefix rep:
011E00BA EB 02 jmp short 011E00BE
011E00BC CD20 EB02CD20 vxdcall 20CD02EB
011E00C2 64:EB 02 jmp short 011E00C7
011E00C5 CD20 8D5AB08D vxdcall 8DB05A8D
011E00CB 5C pop esp
011E00CC 2E:6B2B DD imul ebp,dword ptr cs:[ebx],-23
011E00D0 64:EB 02 jmp short 011E00D5
011E00D3 CD20 83EB6B26 vxdjump 266BEB83
011E00D9 EB 02 jmp short 011E00DD
011E00DB CD20 83C3288B vxdjump 8B28C383
011E00E1 1BEB sbb ebp,ebx
011E00E3 02CD add cl,ch
011E00E5 208D 5BFB8D9B and byte ptr ss:[ebp+9B8DFB5B],cl
011E00EB 64:0800 or byte ptr fs:[eax],al
011E00EE 0053 BB add byte ptr ds:[ebx-45],dl
011E00F1 D6 salc
011E00F2 4A dec edx
011E00F3 40 inc eax
011E00F4 0083 EB472BDB add byte ptr ds:[ebx+DB2B47EB],al
011E00FA 68 C40EF700 push 0F70EC4
011E00FF 36:EB 01 jmp short 011E0103
011E0102 C7C1 EB1803DB mov ecx,DB0318EB
011E0108 5B pop ebx
011E0109 53 push ebx
011E010A C1CB 35 ror ebx,35
011E010D F2: prefix repne:
011E010E EB 01 jmp short 011E0111
011E0110 F0:035C24 38 lock add ebx,dword ptr ss:[esp+38] ; 不允许锁定前缀
011E0115 33F3 xor esi,ebx
011E0117 68 FC84F500 push 0F584FC
011E011C C1CE E1 ror esi,0E1
011E011F BE BADA4600 mov esi,46DABA
011E0124 5E pop esi
011E0125 FFD6 call esi<-------------------------------------从这儿再跟进去,F7
011E0127 68 82E7B126 push 26B1E782
011E012C BE 56824500 mov esi,458256
011E0131 F2: prefix repne:
011E0132 EB 01 jmp short 011E0135
到这儿,第二层
00F584FC 55 push ebp
00F584FD 8BEC mov ebp,esp
00F584FF 83C4 F8 add esp,-8
00F58502 53 push ebx
00F58503 56 push esi
00F58504 57 push edi
00F58505 8B5D 08 mov ebx,dword ptr ss:[ebp+8]
00F58508 EB 01 jmp short 00F5850B
00F5850A 9A 8B451883 E80>call far 08E8:8318458B
00F58511 8B00 mov eax,dword ptr ds:[eax]
00F58513 50 push eax
00F58514 8A8B 8E000000 mov cl,byte ptr ds:[ebx+8E]
00F5851A 8B55 14 mov edx,dword ptr ss:[ebp+14]
00F5851D 8BC3 mov eax,ebx
00F5851F E8 B4FFFFFF call 00F584D8
00F58524 8B45 18 mov eax,dword ptr ss:[ebp+18]
00F58527 50 push eax
00F58528 B1 04 mov cl,4
00F5852A 8B55 14 mov edx,dword ptr ss:[ebp+14]
00F5852D 8BC3 mov eax,ebx
00F5852F E8 A4FFFFFF call 00F584D8
00F58534 EB 01 jmp short 00F58537
00F58536 698B 73308B7B 1>imul ecx,dword ptr ds:[ebx+7B8B3073],97E4A11>
00F58540 F5 cmc
00F58541 008B 4034FFD0 add byte ptr ds:[ebx+D0FF3440],cl
00F58547 2945 0C sub dword ptr ss:[ebp+C],eax
00F5854A 8B45 0C mov eax,dword ptr ss:[ebp+C]
00F5854D 2B43 18 sub eax,dword ptr ds:[ebx+18]
00F58550 2B43 68 sub eax,dword ptr ds:[ebx+68]
00F58553 8945 FC mov dword ptr ss:[ebp-4],eax
00F58556 8D43 24 lea eax,dword ptr ds:[ebx+24]
00F58559 8945 F8 mov dword ptr ss:[ebp-8],eax
00F5855C 85FF test edi,edi
00F5855E 76 38 jbe short 00F58598
00F58560 EB 01 jmp short 00F58563
00F58562 C7 ??? ; 未知命令
00F58563 8B45 F8 mov eax,dword ptr ss:[ebp-8]
00F58566 0FB600 movzx eax,byte ptr ds:[eax]
00F58569 8B5483 40 mov edx,dword ptr ds:[ebx+eax*4+40]
00F5856D 8BC6 mov eax,esi
00F5856F FFD2 call edx
00F58571 3B45 FC cmp eax,dword ptr ss:[ebp-4]
00F58574 75 1A jnz short 00F58590
00F58576 8B45 10 mov eax,dword ptr ss:[ebp+10]
00F58579 50 push eax
00F5857A 8B45 14 mov eax,dword ptr ss:[ebp+14]
00F5857D 50 push eax
00F5857E E8 19FAFFFF call 00F57F9C
00F58583 50 push eax
00F58584 8BCE mov ecx,esi
00F58586 8B55 18 mov edx,dword ptr ss:[ebp+18]
00F58589 8BC3 mov eax,ebx
00F5858B E8 D4FDFFFF call 00F58364<------------------------------------从这儿F7进去
00F58590 4F dec edi
00F58591 0373 6C add esi,dword ptr ds:[ebx+6C]
00F58594 85FF test edi,edi
00F58596 ^ 77 CB ja short 00F58563
00F58598 68 B485F500 push 0F585B4 ; ASCII "111"
00F5859D E8 7ACAFEFF call 00F4501C
到这儿,第三层
00F58364 55 push ebp
00F58365 8BEC mov ebp,esp
00F58367 83C4 F4 add esp,-0C
00F5836A 53 push ebx
00F5836B 56 push esi
00F5836C 57 push edi
...
...
...
011F00AA 2BF1 sub esi,ecx
011F00AC F3: prefix rep:
011F00AD EB 02 jmp short 011F00B1
011F00AF CD20 F2EB01E9 vxdjump E901EBF2
011F00B5 FF36 push dword ptr ds:[esi]
011F00B7 337424 08 xor esi,dword ptr ss:[esp+8]
011F00BB BE 1ABE4100 mov esi,41BE1A
011F00C0 5E pop esi
011F00C1 9D popfd
011F00C2 5C pop esp
011F00C3 FF6424 FC jmp dword ptr ss:[esp-4]<---------------------------在这儿下断,里面就是要找的地址了
011F00C7 382B cmp byte ptr ds:[ebx],ch
如果这个地址还在壳内,说明自定义函数,则继续往下跟;如果往代码段去了,一般都是系统函数,照搬就行了:如,下面是我修复的部分代
码:
004B0077 8B00 mov eax,dword ptr ds:[eax]
004B0079 A3 A0BD5000 mov dword ptr ds:[50BDA0],eax
004B007E E8 3583FDFF call XXXXXXXX.004883B8//这个是系统函数
004B0096 59 pop ecx
004B0097 E8 B41F0200 call XXXXXXXX.004D2050//这个是自定义函数
--->下面也是按照壳内的思路跟出来的
004D2050 68 00000300 push 30000
004D2055 68 00000100 push 10000 ; UNICODE "=::=::\"
004D205A E8 35E2FDFF call <jmp.&msvcrt._controlfp>
004D205F 59 pop ecx
004D2060 59 pop ecx
004D2061 C3 retn
跳转
004D206E 3AC3 cmp al,bl
004D2070 75 0E jnz short XXXXXXXX.004D2080<------------------
004D2072 0000 add byte ptr ds:[eax],al
004D2074 0000 add byte ptr ds:[eax],al
004D2076 0000 add byte ptr ds:[eax],al
004D2078 0000 add byte ptr ds:[eax],al
004D207A 0000 add byte ptr ds:[eax],al
004D207C 0000 add byte ptr ds:[eax],al
004D207E 0000 add byte ptr ds:[eax],al
004D2080 3C 22 cmp al,22
004D2082 ^ 75 E4 jnz short XXXXXXXX.004D2068<------------------
004D2084 EB 0A jmp short XXXXXXXX.004D2090
大体的修复过程就是这样。。。
非常之夸张,全部被偷的代码大概有110多行,但aspr写出来怎么也有个千把行,可想而之垃圾有多少。。。需要注意的是,里面的条件判断,
对于是或否都要跟进去,这样才能把所有的代码都找回来,不然,如果换个条件就不能运行了?
修复完毕后,保存,运行,OK。就是退出时显示非法操作,看来还有些地方没修复好,不过不伤大雅。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!