首页
社区
课程
招聘
[原创]单步跟踪ASProtect Version 1.4(未完,更新到申请TLS的部分)
发表于: 2008-4-16 19:29 6028

[原创]单步跟踪ASProtect Version 1.4(未完,更新到申请TLS的部分)

2008-4-16 19:29
6028
程序就是从ASProtect官方网站下的DEMO版本,
加密的对象是罗云彬书的第四章的FirstWindows程序
等可以发附件了,再发加密程序上来
00401000 A>/$  68 01404000 push ASP_Firs.00404001
00401005   |.  E8 01000000 call ASP_Firs.0040100B
0040100A   \.  C3          retn
0040100B    $  C3          retn

绕了一下,返回到了压入的地址00404001

00404001     60            pushad
00404002     E8 03000000   call ASP_Firs.0040400A                ;跳到了程序中间
来到这里:
0040400A     5D            pop ebp                             ; 堆栈 [0012FFA0]=00404007
0040400B     45            inc ebp
0040400C     55            push ebp
0040400D     C3            retn                                        ;返回到了00404008

这里是把堆栈的值取出来+1又放回去,这样执行retn指令时,返回的地址就+1了=00404007+1=00404008
这里我们不要看到跳到程序中间去,就nop掉,这些代码都是壳精心设计的,不是花指令

00404008    /EB 04         jmp short ASP_Firs.0040400E
0040400A    |5D            pop ebp                             ; 堆栈 [0012FFA0]=00404007
0040400B    |45            inc ebp
0040400C    |55            push ebp
0040400D    |C3            retn
0040400E    \E8 01000000   call ASP_Firs.00404014               

到这里壳还是什么都没有做的,F7进入

00404014     5D            pop ebp                             ; 堆栈 [0012FFA0]=00404013
00404015     BB EDFFFFFF   mov ebx,-13                         ; ebx=FFFFFFED
0040401A     03DD          add ebx,ebp                         ; ebx=00404000
0040401C     81EB 00400000 sub ebx,4000                                ;ebx=00400000
00404022     807D 4D 01    cmp byte ptr ss:[ebp+4D],1                ;[00404060]=00
00404026    /75 0C         jnz short ASP_Firs.00404034                ;这里跳转了

这里是壳通过预先的判定减去13后,得到了程序运行的虚拟地址,然后对00404060的值进行比较,

00404034     8D45 53       lea eax,dword ptr ss:[ebp+53]       ; 地址=00404066
00404037     50            push eax                                ;00404066
00404038     53            push ebx                                ;00400000
00404039     FFB5 F9090000 push dword ptr ss:[ebp+9F9]         ; ss:[00404A0C]=GetModuleHandleA

这里就开始压入GetModuleHandleA函数了,同时我们要注意到上面的寻址方式都是采用ebp+某数来寻址,可见在这个壳里ebp的值至关重要,是壳定位代码的基址,同时我们在数据窗口中跟随00404A00的数据,在用Peditor来看看加壳的程序,就可以发现00404A0C就是kernel32.dll库的IAT表,GetModuleHandleA函数是其中三个之中的第二个。

0040403F     8D45 35       lea eax,dword ptr ss:[ebp+35]       ; 地址=00404048
00404042     50            push eax
00404043     E9 82000000   jmp ASP_Firs.004040CA

保存了好几个值以后跳转了,保存在堆栈中的值如下:
0012FF94   00404048  ASP_Firs.00404048
0012FF98   7C80B529  kernel32.GetModuleHandleA
0012FF9C   00400000  ASP_Firs.00400000
0012FFA0   00404066  ASP_Firs.00404066

004040CA     0FBFC8        movsx ecx,ax                        ; 把ex=4048传入ecx=00004048
004040CD     E8 08000000   call ASP_Firs.004040DA

004040EC     66:81CE E80B  or si,0BE8
004040F1     2BD2          sub edx,edx
004040F3     66:81D7 39E0  adc di,0E039
004040F8     8B0413        mov eax,dword ptr ds:[ebx+edx]      ; ds:[00404918]=3298ACBE
004040FB     66:8BFB       mov di,bx
004040FE     81F0 6E40D86D xor eax,6DD8406E
00404104     56            push esi
00404105     8BF3          mov esi,ebx                         ; esi=ebx=00404918
00404107     59            pop ecx
00404108     81C0 0F23C96B add eax,6BC9230F
0040410E     66:8BF3       mov si,bx
00404111     81E8 9CAFEC19 sub eax,19ECAF9C
00404117     890413        mov dword ptr ds:[ebx+edx],eax      ; eax=B11D6043
0040411A     E8 0C000000   call ASP_Firs.0040412B

上面这段代码,经过一系列运算后更改了一个内存值【00404918】从3298ACBE变为B11D6043

0040412B     E8 0B000000   call ASP_Firs.0040413B

0040413B     81C1 664BBE15 add ecx,15BE4B66
00404141     5E            pop esi                                        ;00404130
00404142     5F            pop edi                                        ;0040411F
00404143     BE 9F7B0B0C   mov esi,0C0B7B9F
00404148     83EA 04       sub edx,4                          ; edx=FFFFFFFC
0040414B     E9 10000000   jmp ASP_Firs.00404160

esi和edi得到的就是前面两个call压入堆栈的返回地址

00404160     81FA 50F8FFFF cmp edx,-7B0
00404166   ^ 0F85 8CFFFFFF jnz ASP_Firs.004040F8                ;跳回去了,有循环

我们可以发现壳获得数据是这样的[ebx+edx],通过改变edx的值把它减去4,我们就可以变换读取的地址,这个循环的计数器就是edx,这段循环取出一个内存值计算后又存了进去,是一段解密代码,F4到下一行跳出循环,同时得出循环的作用:修改代码,范围00404918--0040416C,大小0x7B0,他修改的代码就在我们循环的下面,你可以看到OD中代码的改变,我们跳出循环后,就开始执行解密后的代码

0040416C     66:8BDE       mov bx,si                          ; ebx=00407B9F
0040416F     E8 05000000   call ASP_Firs.00404179

修改了ebx的值=00407B9F,继续

00404179     E8 0D000000   call ASP_Firs.0040418B

连续的两个call调用,我们要记住每一个call调用,都会在堆栈中放一个返回值。

0040418B     8AF1    mov dh,cl
0040418D     5A      pop edx
0040418E     58      pop eax
0040418F     68 B2A3>push 0ADCA3B2
00404194     66:B9 5>mov cx,0A85F
00404198     5B      pop ebx
00404199     81C0 A6>add eax,7A6
0040419F     81E9 44>sub ecx,1FEBCB44
004041A5     BE BD01>mov esi,1BD
004041AA     E8 0500>call ASP_Firs.004041B4

这段代码就是修改了一些寄存器的值,继续

004041B4     66:81CB>or bx,0F147
004041B9     5B      pop ebx
004041BA     8B38    mov edi,dword ptr ds:[eax]         ; ds:[0040491A]=E9B8B11D
004041BC     81F7 8F>xor edi,3F08138F
004041C2     8BDE    mov ebx,esi
004041C4     81C7 1C>add edi,3C00161C
004041CA     66:8BDE mov bx,si
004041CD     81EF 25>sub edi,486E2225
004041D3     66:8BD8 mov bx,ax
004041D6     8938    mov dword ptr ds:[eax],edi                ;把计算后的值放回去了

看到这一条,我们就可以猜测马上又要有一个修改代码的循环了,因为上面的代码,从一个内存地址取出值计算后又把它放了回去。关于循环,我们要注意一个就是它的作用,还有就是它的计数器,继续

004041D8     66:B9 4>mov cx,3441
004041DC     81E8 27>sub eax,253E9F27
004041E2     66:81C2>add dx,2A79
004041E7     81C0 23>add eax,253E9F23
004041ED    /0F8B 05>jpo ASP_Firs.004041F8
004041F3    |BA 5872>mov edx,49AA7258
004041F8    \83EE 01 sub esi,1                          ; 通过减去esi的值来判断是否跳回去循环,esi就是循环的计数器
004041FB   ^ 0F85 B9>jnz ASP_Firs.004041BA
00404201     E8 1200>call ASP_Firs.00404218

循环作用:修改代码,范围0040491A--0040422A,大小0x1BD
这里我们要看到eax是循环读取地址的指针,程序里是把它减去253E9F27又加上253E9F23,这两个数之间相差是4,它就相当于直接减4,我们不要看着两个数比较大,就不注意这些。

00404218     50      push eax                           ; ASP_Firs.00404226
00404219     E9 0A00>jmp ASP_Firs.00404228

保存了eax,里面的值就是上面解密后的数据前一个双字

00404228     5A      pop edx
00404229     5A      pop edx
0040422A     E8 0700>call ASP_Firs.00404236

这里的pop就只是为了平衡堆栈,同时我们可以发现前面eax压入的值完全无用,可见00404218是一条垃圾指令,经过两条后堆栈的值如下:
0012FF94   00404048  ASP_Firs.00404048
0012FF98   7C80B529  kernel32.GetModuleHandleA
0012FF9C   00400000  ASP_Firs.00400000
0012FFA0   00404066  ASP_Firs.00404066
如果你还记得的话,这几个值就是前面压入GetModuleHandleA函数后,一直没有变化。
同时我们还要注意到,我们现在已经来到0040422A了,也就是已经开始执行后面循环解密的代码了

00404236     51      push ecx
00404237     66:8BFA mov di,dx
0040423A     5F      pop edi
0040423B     5B      pop ebx
0040423C     51      push ecx
0040423D     E9 0A00>jmp ASP_Firs.0040424C

没有什么有用的东西,继续

0040424C     5A      pop edx
0040424D     81C3 EB>add ebx,6EB                        ; ebx=0040491A
00404253     E9 1100>jmp ASP_Firs.00404269

你可以注意到,壳虽然不停的在push,但也不断的pop出值,它尽力的在保持堆栈里面的那四个值在上面,可见这一定是很重要的东西了,同时在这里他给ebx=0040491A

00404269     B9 8001>mov ecx,180                        ; 给ecx赋值时我们一定要注意是不是循环又要来了
0040426E     81EA 07>sub edx,5FC68707
00404274     8B33    mov esi,dword ptr ds:[ebx]         ; ds:[0040491A]=CA429689
00404276     0F8C 00>jl ASP_Firs.0040427C
0040427C     81EE F3>sub esi,23FAB8F3
00404282     66:81E2>and dx,0ED1B
00404287     81F6 B0>xor esi,750CF9B0
0040428D     E9 0B00>jmp ASP_Firs.0040429D

0040429D     81F6 29>xor esi,18BA2029
004042A3     E9 0D00>jmp ASP_Firs.004042B5

004042B5     8933    mov dword ptr ds:[ebx],esi         ; 和上面的读取指令配合,读出内容经过计算后又放回去,可见解密代码的循环来了
004042B7     8AC6    mov al,dh
004042B9     83EB 03 sub ebx,3
004042BC     66:81DF>sbb di,8965
004042C1     4B      dec ebx
004042C2     0F88 11>js ASP_Firs.004042D9
004042C8     E9 0C00>jmp ASP_Firs.004042D9

004042D9     83E9 01 sub ecx,1                                ;计数器好像在这里0x180-1=0x17F,但是下面的循环是往下跳转的,继续看看
004042DC     0F85 1A>jnz ASP_Firs.004042FC

004042FC    /0F8D 05>jge ASP_Firs.00404307                ;这里也是肯定跳的

00404307   ^\E9 68FF>jmp ASP_Firs.00404274                ;这里跳回去了

原来壳绕了一下,最后还是要跳回去循环的,循环的作用和计数器都找到了,我们还要看看ebx是读取地址的指针,他是怎么被修改的,原来在这里:
004042B9     83EB 03 sub ebx,3
004042BC     66:81DF>sbb di,8965
004042C1     4B      dec ebx
先减去3又减去1,总的就是减去了4,因为程序是一个双字=4字节的解密,所以我们就已经可以确定这个循环的作用:修改代码,范围0040491A--0040431E,大小0x180.
要说的是,这里我们要跳出循环的话,我们要从004042DC的下面F4,如何知道的呢?因为如果循环结束的话,这里将是第一个跳转的。
004042DC    /0F85 1A>jnz ASP_Firs.004042FC
004042E2    |50      push eax                           ; F4到这里
004042E3     5A      pop edx                            ; edx=004042AD
004042E4     E9 3500>jmp ASP_Firs.0040431E

跳转下去的地方就是刚才循环解密的代码,经过着三个循环我们可以发现,壳都是解密一点又去执行一点,然后又解密又执行,而在这中间,实际上壳什么也没有做,下面可以猜测可能还是要解密数据的。

0040431E     E8 0B00>call ASP_Firs.0040432E

0040432E     66:8BD8 mov bx,ax                          ; ebx=004042AD
00404331     59      pop ecx                            ; ASP_Firs.00404323
00404332     E8 1200>call ASP_Firs.00404349

看来壳是绝对不肯破坏堆栈里的那四个值,所以push和pop,call和pop都是要配对出现。

00404349     56      push esi                                ;esi=00000BE8
0040434A     E8 0A00>call ASP_Firs.00404359

00404359     66:BF 7>mov di,0F78                        ; edi=0040434F
0040435D     5F      pop edi
0040435E     59      pop ecx
0040435F     5E      pop esi                            ; ASP_Firs.00404337
果然又是三个pop,堆栈里还是前面粘贴的那四个值
00404360     8BCF    mov ecx,edi
00404362     81C6 E0>add esi,5E0
00404368     8BFA    mov edi,edx
0040436A     B8 C175>mov eax,794F75C1
0040436F     81E8 75>sub eax,794F7475
00404375     8B16    mov edx,dword ptr ds:[esi]         ; ds:[00404917]=0F43543C
00404377     0FBFDE  movsx ebx,si
0040437A     81EA 59>sub edx,3E2CEF59
00404380     8ACC    mov cl,ah
00404382     81EA 1E>sub edx,61E5371E
00404388     81C9 F0>or ecx,73A6DBF0
0040438E     81F2 FF>xor edx,4497CBFF
00404394     E8 1400>call ASP_Firs.004043AD

我们对他的运算过程并不感兴趣,这里也是我们只要知道他读入了一个内存值,然后进行了计算就可以了

004043AD     5B      pop ebx
004043AE     8916    mov dword ptr ds:[esi],edx         ; 和读取指令配合,把计算后的数据又存了回去,可见这又是一个修改代码的了
004043B0     81EE 81>sub esi,1FD14B81
004043B6     81C6 7D>add esi,1FD14B7D                        ;你要注意到这里的两个数据相差还是4,实际就是esi-4,让内存地址指针指向下一个地址
004043BC     8BD8    mov ebx,eax
004043BE     83E8 01 sub eax,1                          ; 这次的计数器是eax=0x14C
004043C1     0F85 14>jnz ASP_Firs.004043DB

004043DB     B7 2D   mov bh,2D
004043DD   ^ E9 93FF>jmp ASP_Firs.00404375

还是一样的,计数器减去1后,并不马上跳转,先来个向下跳转,然后才jmp回去执行循环,一样的我们F4跳出循环,F4应该在jnz指令的下面,而不是jmp
循环作用:修改代码,范围00404917--004043EB,大小0x14C(这个壳好像就是喜欢倒着修改数据)

004043C7     68 0A50>push 1EC9500A
004043CC     8BDF    mov ebx,edi                        ; edi=004042AD (ASP_Firs.004042AD)
004043CE     5B      pop ebx
004043CF     E9 1700>jmp ASP_Firs.004043EB

没有什么,跳转的地址就是刚刚修改的代码地址,继续

004043EB     E8 0000>call ASP_Firs.004043F0
004043F0     5D      pop ebp                                ;这里平衡堆栈
004043F1     5B      pop ebx                            ; 这里大不同了,它pop得到的是前面压入的四个数据
004043F2     895D 5B mov dword ptr ss:[ebp+5B],ebx
004043F5     5B      pop ebx
004043F6     895D 5F mov dword ptr ss:[ebp+5F],ebx
004043F9     58      pop eax
004043FA     8985 14>mov dword ptr ss:[ebp+414],eax
00404400     58      pop eax
00404401     807D 5A>cmp byte ptr ss:[ebp+5A],1         ; ss:[0040444A]=00
00404405     75 59   jnz short ASP_Firs.00404460        ;跳了

你已经忘记了吗?我们来看看它要把这四个看起来很重要的值放到哪里:
0012FF94   00404048  ASP_Firs.00404048                                ;0040444B
0012FF98   7C80B529  kernel32.GetModuleHandleA                        ;0040444F
0012FF9C   00400000  ASP_Firs.00400000                                ;00404804
0012FFA0   00404066  ASP_Firs.00404066                                ;eax
比较的内存地址就是第一个堆栈数存取的前一个字节

00404460     E8 A302>call ASP_Firs.00404708                        ;这个是长距离的调用了,不同于前面的call

00404708     8B4424 >mov eax,dword ptr ss:[esp+24]      ; 堆栈 ss:[0012FFC4]=7C816D4F
0040470C     25 0000>and eax,FFFF0000
00404711     05 0000>add eax,10000                      ; UNICODE "=::=::\"
00404716     2D 0000>sub eax,10000                      ;
0040471B     66:8138>cmp word ptr ds:[eax],5A4D
00404720   ^\75 F4   jnz short ASP_Firs.00404716

获得程序从系统中得到的返回kernel32.dll的值,然后把他按照0x00010000来对其,再比较是否等于5A4D,你或许想不到他要做什么,不过我们来看看5A4D的ASCII码是多少“ZM”,这个是PE文件的DOS文件头标志,原来这就是传说中的动态获取Kernel32.dll,关于他的原理不知道的可以去网上找找资料,很多的,我手里有着的罗云彬书的最后一章就有一个例子程序。壳通过动态得到Kernel32.dll后再得到LoadLibrary和GetProcAddress函数,然后就可以利用这两个函数,得到任何想要的函数了。
循环的比较,知道得到Kernel32.dll的系统地址
F4跳出循环后,可以看到壳已经得到了,eax=7C800000

00404722     60      pushad
00404723     8985 FF>mov dword ptr ss:[ebp+3FF],eax     ; 把Kernel32.dll的地址存到【004047EF】
00404729     8BD0    mov edx,eax
0040472B     8BD8    mov ebx,eax                        ; eax,ebx,edx全部放入这个地址
0040472D     0340 3C add eax,dword ptr ds:[eax+3C]      ; 现在eax指向的就是Kernel32.dll的DOS文件头,+3C就来到了e_lfanew字段
00404730     0358 78 add ebx,dword ptr ds:[eax+78]      ; PE文件头+78就来到了IMAGE_DIRECTORY_ENTRY_EXPORT(导出表)
00404733     899D 14>mov dword ptr ss:[ebp+314],ebx     ; 把Kernel32.dll的导出表地址存到【00404704】
00404739     8D9D D3>lea ebx,dword ptr ss:[ebp+3D3]     ; ebx=004047C3
0040473F     8DBD EB>lea edi,dword ptr ss:[ebp+3EB]     ; edi=004047DB
00404745     8B33    mov esi,dword ptr ds:[ebx]         ; esi=B72661A7
00404747     89B5 83>mov dword ptr ss:[ebp+383],esi
0040474D     E8 0B00>call ASP_Firs.0040475D

0040475D     60  pushad
0040475E     8B9>mov ebx,dword ptr ss:[ebp+314]     ; ss:[00404704]=7C80262C,这个就是Kernel32.dll的导出表地址
00404764     8B4>mov ecx,dword ptr ds:[ebx+20]      ; 导出表+20就来到了AddressofNames字段
00404767     03C>add ecx,edx                        ; 基址+RVA就得到指向这个导出表的模块名称字符串
00404769     8B3>mov esi,dword ptr ds:[ecx]         ; 得到函数名字符串地址表的RVA
0040476B     03F>add esi,edx                        ; 基址+RVA,得到函数名字符串地址表
0040476D     E8 >call ASP_Firs.004047A1

你能明白吗?不能的话就要去加强了解PE可执行文件的结构了,现在esi已经指向名称字符串地址表的第一个字符串了
esi=7C804B73

004047A1     52  push edx                           ; 把Kernel32.dll的基址存起来,因为下面要用到edx了
004047A2     BA >mov edx,9C3B248E
004047A7     AC  lods byte ptr ds:[esi]
004047A8     0AC>or al,al
004047AA     74 >je short ASP_Firs.004047C0
004047AC     32D>xor dl,al
004047AE     B0 >mov al,8
004047B0     D1E>shr edx,1
004047B2     73 >jnb short ASP_Firs.004047BA
004047B4     81F>xor edx,C1A7F39A
004047BA     FEC>dec al
004047BC   ^ 75 >jnz short ASP_Firs.004047B0
004047BE   ^ EB >jmp short ASP_Firs.004047A7
004047C0     92  xchg eax,edx                       ; edx得到一个用函数的字符串名字计算出来的值
004047C1     5A  pop edx
004047C2     C3  retn                                        ;返回到00404752

这里就是用名称字符串地址表的字符串计算的到一个值,然后返回

00404772     BF >mov edi,B72551A7
00404777     3BC>cmp eax,edi                        ; 把得到的值和壳中的一个固定值比较
00404779     74 >je short ASP_Firs.00404780
0040477B     83C>add ecx,4                          ; 增加ecx,里面就是指向函数名字符串地址表的RVA表
0040477E   ^ EB >jmp short ASP_Firs.00404769                ;不断的找,直到得到要找的函数

我们已经明白了,B72551A7就是壳已经预先计算好了的值,他要看看现在指向的函数是不是它想要的函数
我们跳出循环,赶快去看看壳到底想要得到的是一个什么函数
跳出循环后
ECX=7C803B84 ASCII "Yj"我们要找的就是【7C803B84】=00006A59,7C806A59里的函数字符串,通过在OD的数据窗口中,我们找到了,原来是GetProcAddress函数

00404780     2B4>sub ecx,dword ptr ds:[ebx+20]
00404783     2BC>sub ecx,edx
00404785     D1E>shr ecx,1                          ; 得到了函数在地址表中的序号
00404787     034>add ecx,dword ptr ds:[ebx+24]      ; 导入表地址+24就得到了AddressOfNameOrdinals字段,+地址表中的函数序号,
0040478A     03C>add ecx,edx                        ; 得到了AddressOfNameOrdinals字段中函数的值
0040478C     0FB>movzx ecx,word ptr ds:[ecx]        ; 得到了函数的AddressOfFunctions字段指向的地址表的函数序号
0040478F     C1E>shl ecx,2                          ; 乘以4,把双字变为字节
00404792     034>add ecx,dword ptr ds:[ebx+1C]      ; 导出表+1C就来到了AddressOfFunctions字段了,加上ecx中的序号,
00404795     03C>add ecx,edx                        ; 加上基址,ecx得到了入口地址的RVA
00404797     8B0>mov ecx,dword ptr ds:[ecx]         ; 得到函数入口地址的RVA
00404799     03C>add ecx,edx                        ; 得到函数的虚拟地址了
0040479B     894>mov dword ptr ss:[esp+1C],ecx      ; ecx=7C80AC28 (kernel32.GetProcAddress)拿到了,把它存到0012FF78
0040479F     61  popad
004047A0     C3  retn

终于跟完了,如果不理解导出表的结构,根本就不知道在做什么,呵呵,继续

00404752     AB  stos dword ptr es:[edi]            ; 函数的系统地址存到了004047DB
00404753     83C>add ebx,4
00404756     833>cmp dword ptr ds:[ebx],0
00404759   ^ 75 >jnz short ASP_Firs.00404745
0040475B     61  popad
0040475C     C3  retn

把数值存起来,比较看看是不是完了,没有就继续,我们就不循环的跟了,直接看看壳所有取的函数及存储地址
004047DB  GetProcAddress
004047DF  GetModuleHandleA
004047E3  LoadLibraryA
004047E7  VirtualAlloc
004047EB  VirtualFree

00404745     8B3>mov esi,dword ptr ds:[ebx]         ; esi=B72661A7
00404747     89B>mov dword ptr ss:[ebp+383],esi     ; 存入了【00404773】
0040474D     E8 >call ASP_Firs.0040475D                        ;F8不过这里就可以在下一条指令里看到取得的函数
00404752     AB  stos dword ptr es:[edi]            ; 函数的系统地址存到了004047DB
00404753     83C>add ebx,4
00404756     833>cmp dword ptr ds:[ebx],0
00404759   ^ 75 >jnz short ASP_Firs.00404745
0040475B     61  popad
0040475C     C3  retn                                        ;从这里返回到00404465

00404465     FC  cld
00404466     8DB>lea esi,dword ptr ss:[ebp+8C]      ; 地址=0040447C
0040446C     AD  lods dword ptr ds:[esi]            ; ds:[esi]=[0040447C]=00401000
0040446D     0BC>or eax,eax
0040446F     74 >je short ASP_Firs.0040448C         ; 得到00401000,这个就是原程序的入口
00404471     8BF>mov edi,eax
00404473     B9 >mov ecx,0C
00404478     F3:>rep movs byte ptr es:[edi],byte ptr ds:[esi]    ; 把00404480处0x0C大小的字节数据移动到00401000
0040447A     EB >jmp short ASP_Firs.0040448C

移动过去的数据如下:
00401000 >13 BA B3 19 90 00 00 00 C1 8F 29 C6              _撼_?..翉)铺1稯

0040448C     89A>mov dword ptr ss:[ebp+430],esp                  ; esp=0012FFA4
00404492     6A >push 40
00404494     68 >push 1000
00404499     FFB>push dword ptr ss:[ebp+40F]                                ;00035000
0040449F     6A >push 0
004044A1     FF9>call dword ptr ss:[ebp+3F7]                     ; kernel32.VirtualAlloc

004044A7     8985 D3>mov dword ptr ss:[ebp+1D3],eax                  ; 返回的动态内存基址存入【004045C3】
004044AD     8B9D 07>mov ebx,dword ptr ss:[ebp+407]                  ; ss:[004047F7]=00004B38
004044B3     039D 14>add ebx,dword ptr ss:[ebp+414]                  ; 00404B38
004044B9     50      push eax                                                ;返回的基址
004044BA     53      push ebx                                                ;00404B38
004044BB     E8 0B01>call ASP_Firs.004045CB

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (5)
雪    币: 47147
活跃值: (20465)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
[QUOTE='UD]Arthas;442094']程序就是从ASProtect官方网站下的DEMO版本,
加密的对象是罗云彬书的第四章的FirstWindows程序
等可以发附件了,再发加密程序上来

...[/QUOTE]

再发几帖你就有权限了。
这种将调试过程记录下来学习方法不错,但感觉你文章结构不是太清晰。
2008-4-16 20:37
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
谢了,我只所以产生发单步跟踪记录过程,就是因为我觉得,实际上那些告诉
到这里F7,到哪里F8,这里要修改,那里是seh的,到xxxxx处dump的类似的文章,对于新手的话很难学到东西,而我的单步跟踪问题就是坛主说的,你很难再去把他写的结构清晰明了,毕竟每一条指令都包含着,我的这一篇比起上一篇来说在一些基本的细节上,已经是不再注释了,但还是很混乱。
新手看我这个文章,要想学到东西的话,要等着我把加密的程序发上来,这个时候就能感受到它的好处了,你可以边开着OD,边看我的记录,每一小段指令,只要是有必要注释的我全部都注释着,让你能很好的学习。
比如说我觉得我上面的这一段代码,新手看的话,尤其要学习00404708到004047A0之间的部分,这是一段动态获取Kernel32.dll连接库,然后再得到里面的LoadLibrary和GetProAddress函数,
有了他们什么函数就都可以得到了,当然虽然在上面我加了很好的注释,但是你还是要熟悉PE结构,熟悉导出表。
这段代码不止在当年,即使是现在也是一段精妙的代码,学会它有很大的意义和好处,推荐新手多看看
2008-4-16 22:07
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
分配的动态地址的基址=003C0000

004045CB     55         push ebp                             ; ebp=004043F0
004045CC     8BEC       mov ebp,esp
004045CE     60         pushad
004045CF     55         push ebp
004045D0     8B75 08    mov esi,dword ptr ss:[ebp+8]         ; 堆栈 ss:[0012FF9C]=00404B38
004045D3     8B7D 0C    mov edi,dword ptr ss:[ebp+C]         ; 堆栈 ss:[0012FFA0]=003C0000

这里我们要说的就是【ebp+8】与【ebp+C】这样的读取方式,它实际上读取的就是在执行call前压入堆栈的参数,【ebp+8】是第一个参数,【ebp+C】是第二个我们可以简单的说一下为什么?看一下堆栈结构
0012FF94  \004043F0  返回到 ASP_Firs.004043F0         这个是保存push ebp时压入的值                [ebp+0]
0012FF98   004044C0  返回到 ASP_Firs.004044C0         这个是调用call时压入的返回地址        [ebp+4]
0012FF9C   00404B38  ASP_Firs.00404B38                这个是call前ebx压入的参数                [ebp+8]
0012FFA0   003C0000                                        这个是call前eax压入的参数                [ebp+C]
前面的两个值都是固定要push进去的,所以你要读取call前压入堆栈的数据就要从[ebp+8]开始

004045D6     FC         cld
004045D7     B2 80      mov dl,80
004045D9     8A06       mov al,byte ptr ds:[esi]             ; 读出的数据来自【00404B38】
004045DB     46         inc esi
004045DC     8807       mov byte ptr ds:[edi],al             ; 存入到【003C0000】,也就是动态分配的内存里
004045DE     47         inc edi
004045DF     02D2       add dl,dl
004045E1     75 05      jnz short ASP_Firs.004045E8
004045E3     8A16       mov dl,byte ptr ds:[esi]
004045E5     46         inc esi
004045E6     12D2       adc dl,dl
004045E8   ^ 73 EF      jnb short ASP_Firs.004045D9
004045EA     02D2       add dl,dl
004045EC     75 05      jnz short ASP_Firs.004045F3
004045EE     8A16       mov dl,byte ptr ds:[esi]
004045F0     46         inc esi
004045F1     12D2       adc dl,dl
004045F3     73 4A      jnb short ASP_Firs.0040463F
004045F5     33C0       xor eax,eax
004045F7     02D2       add dl,dl
004045F9     75 05      jnz short ASP_Firs.00404600
004045FB     8A16       mov dl,byte ptr ds:[esi]
004045FD     46         inc esi
004045FE     12D2       adc dl,dl
00404600     0F83 D6000>jnb ASP_Firs.004046DC
00404606     02D2       add dl,dl
00404608     75 05      jnz short ASP_Firs.0040460F
0040460A     8A16       mov dl,byte ptr ds:[esi]
0040460C     46         inc esi
0040460D     12D2       adc dl,dl
0040460F     13C0       adc eax,eax
00404611     02D2       add dl,dl
00404613     75 05      jnz short ASP_Firs.0040461A
00404615     8A16       mov dl,byte ptr ds:[esi]
00404617     46         inc esi
00404618     12D2       adc dl,dl
0040461A     13C0       adc eax,eax
0040461C     02D2       add dl,dl
0040461E     75 05      jnz short ASP_Firs.00404625
00404620     8A16       mov dl,byte ptr ds:[esi]
00404622     46         inc esi
00404623     12D2       adc dl,dl
00404625     13C0       adc eax,eax
00404627     02D2       add dl,dl
00404629     75 05      jnz short ASP_Firs.00404630
0040462B     8A16       mov dl,byte ptr ds:[esi]
0040462D     46         inc esi
0040462E     12D2       adc dl,dl
00404630     13C0       adc eax,eax
00404632     74 06      je short ASP_Firs.0040463A
00404634     57         push edi
00404635     2BF8       sub edi,eax
00404637     8A07       mov al,byte ptr ds:[edi]
00404639     5F         pop edi
0040463A     8807       mov byte ptr ds:[edi],al
0040463C     47         inc edi
0040463D   ^ EB A0      jmp short ASP_Firs.004045DF

我们要说明的是,关于壳是如何解密运算的我们并不感兴趣,我们只要知道它在做什么就可以了
这段代码作用就是经过运算把一些代码从【00404B38】开始的内存地址,移动到【003C0000】
要跳出这样的一个循环,我们要从循环的开始寻找能够跳出循环的条件转移,虽然一般F4到下一行
就可以了,但是并不完全是这样的。这里跳出循环的就是下一行,我们F4下去,就看到edi=
003C000C,这就说明循环里一共移动了0xC的数据(我们从OD的数据窗口跟踪也可以看到)。

00404644     02D2       add dl,dl
00404646     75 05      jnz short ASP_Firs.0040464D
00404648     8A16       mov dl,byte ptr ds:[esi]
0040464A     46         inc esi
0040464B     12D2       adc dl,dl
0040464D     13C0       adc eax,eax
0040464F     02D2       add dl,dl
00404651     75 05      jnz short ASP_Firs.00404658
00404653     8A16       mov dl,byte ptr ds:[esi]             ; 又从esi=【00404B45】里读取了一个字节
00404655     46         inc esi
00404656     12D2       adc dl,dl
00404658   ^ 72 EA      jb short ASP_Firs.00404644
0040465A     83E8 02    sub eax,2
0040465D     75 28      jnz short ASP_Firs.00404687
0040465F     B9 0100000>mov ecx,1
00404664     02D2       add dl,dl
00404666     75 05      jnz short ASP_Firs.0040466D
00404668     8A16       mov dl,byte ptr ds:[esi]
0040466A     46         inc esi
0040466B     12D2       adc dl,dl
0040466D     13C9       adc ecx,ecx
0040466F     02D2       add dl,dl
00404671     75 05      jnz short ASP_Firs.00404678
00404673     8A16       mov dl,byte ptr ds:[esi]
00404675     46         inc esi
00404676     12D2       adc dl,dl
00404678   ^ 72 EA      jb short ASP_Firs.00404664
0040467A     56         push esi
0040467B     8BF7       mov esi,edi
0040467D     2BF5       sub esi,ebp
0040467F     F3:A4      rep movs byte ptr es:[edi],byte ptr ds:[es>
00404681     5E         pop esi
00404682   ^ E9 58FFFFF>jmp ASP_Firs.004045DF
00404687     48         dec eax
00404688     C1E0 08    shl eax,8
0040468B     8A06       mov al,byte ptr ds:[esi]
0040468D     46         inc esi
0040468E     8BE8       mov ebp,eax
00404690     B9 0100000>mov ecx,1
00404695     02D2       add dl,dl
00404697     75 05      jnz short ASP_Firs.0040469E
00404699     8A16       mov dl,byte ptr ds:[esi]
0040469B     46         inc esi
0040469C     12D2       adc dl,dl
0040469E     13C9       adc ecx,ecx
004046A0     02D2       add dl,dl
004046A2     75 05      jnz short ASP_Firs.004046A9
004046A4     8A16       mov dl,byte ptr ds:[esi]
004046A6     46         inc esi
004046A7     12D2       adc dl,dl
004046A9   ^ 72 EA      jb short ASP_Firs.00404695
004046AB     3D 007D000>cmp eax,7D00
004046B0     73 1A      jnb short ASP_Firs.004046CC
004046B2     3D 0005000>cmp eax,500
004046B7     72 0E      jb short ASP_Firs.004046C7
004046B9     41         inc ecx
004046BA     56         push esi
004046BB     8BF7       mov esi,edi
004046BD     2BF0       sub esi,eax
004046BF     F3:A4      rep movs byte ptr es:[edi],byte ptr ds:[es>
004046C1     5E         pop esi
004046C2   ^ E9 18FFFFF>jmp ASP_Firs.004045DF
004046C7     83F8 7F    cmp eax,7F
004046CA     77 03      ja short ASP_Firs.004046CF
004046CC     83C1 02    add ecx,2
004046CF     56         push esi
004046D0     8BF7       mov esi,edi
004046D2     2BF0       sub esi,eax
004046D4     F3:A4      rep movs byte ptr es:[edi],byte ptr ds:[es>
004046D6     5E         pop esi
004046D7   ^ E9 03FFFFF>jmp ASP_Firs.004045DF
004046DC     8A06       mov al,byte ptr ds:[esi]
004046DE     46         inc esi
004046DF     33C9       xor ecx,ecx
004046E1     C0E8 01    shr al,1
004046E4     74 12      je short ASP_Firs.004046F8
004046E6     83D1 02    adc ecx,2
004046E9     8BE8       mov ebp,eax
004046EB     56         push esi                                        ; esi=00404B5D
004046EC     8BF7       mov esi,edi
004046EE     2BF0       sub esi,eax
004046F0     F3:A4      rep movs byte ptr es:[edi],byte ptr ds:[esi]
004046F2     5E         pop esi
004046F3   ^ E9 E7FEFFF>jmp ASP_Firs.004045DF

F4到下一行,跳出这个循环后,可以看到edi已经=003D7E00了,已经填充到这里,我们用OD的数据窗口可以看到,继续

004046F8     5D         pop ebp
004046F9     2B7D 0C    sub edi,dword ptr ss:[ebp+C]                    ; 得到了移进去的数据大小edi=0017E00
004046FC     897D FC    mov dword ptr ss:[ebp-4],edi                    ; 把它存进去,注意它存储的位置,popad后,这个值就被eax得到了
004046FF     61         popad
00404700     5D         pop ebp                                         ; 恢复ebp=004043F0,只是壳定位数据的基址
00404701     C2 0800    retn 8

计算了移进去的数据,然后就返回了
004044BB     E8 0B01000>call ASP_Firs.004045CB      ; 这个call移动数据到新分配的内存里
004044C0     6A 40      push 40                                ;我们返回到了这里
004044C2     68 0010000>push 1000
004044C7     FFB5 0F040>push dword ptr ss:[ebp+40F]                        ;00035000
004044CD     6A 00      push 0
004044CF     FF95 F7030>call dword ptr ss:[ebp+3F7]                     ; kernel32.VirtualAlloc

再分配一块内存

004044D5     8985 38040>mov dword ptr ss:[ebp+438],eax                  ; 这次=00D10000,存到00404828
004044DB     8985 D7010>mov dword ptr ss:[ebp+1D7],eax                  ; 还存到了004045C7,我们翻看一下前面的一块存到了004045C3
004044E1     64:67:A1 0>mov eax,dword ptr fs:[0]                        ; 把FS:【0】的值取到eax=0012FFE0,我们应该知道FS:【0】指向的就是seh的结构,不过这里壳还没有设置seh,所以0012FFE0指向的是系统的SEH结构
004044E6     8985 34040>mov dword ptr ss:[ebp+434],eax          ; 存入【00404824】

这里我们要说的是壳把原来系统seh结构的地址放到【00404824】
                                又把00D1000放到【00404828】
实际上已经在内存里构造了一个EXCEPTION_REGISTRATION结构,如果有必要的时候把【00404824】放到FS:【0】就可以构建好程序的seh结构了,这是不是伏笔呢,我们不知道。继续

004044EC     8B55 5B    mov edx,dword ptr ss:[ebp+5B]           ; ss:[0040444B]=00404048 还记得吗这个就是壳前期一直在堆栈里保留的四个数据之一
004044EF     8B85 D7010>mov eax,dword ptr ss:[ebp+1D7]          ; ss:[004045C7]=00D10000,得到新分配的内存地址
004044F5     8902       mov dword ptr ds:[edx],eax              ; eax=00D10000 存到了【00404048】,我们要记好,因为感觉壳好像很在意这个地址
004044F7     8B85 0F040>mov eax,dword ptr ss:[ebp+40F]          ; ss:[004047FF]=00035000
004044FD     8942 04    mov dword ptr ds:[edx+4],eax            ; eax=00035000存到了【0040404C】
00404500     8D85 A6030>lea eax,dword ptr ss:[ebp+3A6]          ; 地址=00404796
00404506     8B40 55    mov eax,dword ptr ds:[eax+55]           ; ds:[004047EB]=7C809B14 (kernel32.VirtualFree)
00404509     8942 08    mov dword ptr ds:[edx+8],eax            ; 存到了【00404050】,和上面两个接着的
0040450C     8B85 F3030>mov eax,dword ptr ss:[ebp+3F3]          ; ss:[004047E3]=7C801D77 (kernel32.LoadLibraryA)
00404512     8942 10    mov dword ptr ds:[edx+10],eax
00404515     8B85 EF030>mov eax,dword ptr ss:[ebp+3EF]          ; ss:[004047DF]=7C80B529 (kernel32.GetModuleHandleA)
0040451B     8942 14    mov dword ptr ds:[edx+14],eax

这上面连起来就是把新分配的内存地址和大小存起来,还有函数的地址:
00404040  45 35 50 E9 82 00 00 00 00 00 D1 00 00 50 03 00  E5P閭.....?.P_.
00404050  14 9B 80 7C 00 00 00 00 77 1D 80 7C 29 B5 80 7C  _泙|....w_€|)祤|

0040451E     8B95 D3010>mov edx,dword ptr ss:[ebp+1D3]          ; ss:[004045C3]=003C0000
00404524     BB F801000>mov ebx,1F8                      ; 得到常量0x1F8,下面把它作为偏移量来定位内存地址
00404529     8B7C1A 0C  mov edi,dword ptr ds:[edx+ebx+C]        ; ds:[003C0204]=00001000
0040452D     0BFF       or edi,edi
0040452F     74 1E      je short ASP_Firs.0040454F
00404531     8B4C1A 10  mov ecx,dword ptr ds:[edx+ebx+10]                ; ds:[003C0208]=00014000
00404535     0BC9       or ecx,ecx
00404537     74 11      je short ASP_Firs.0040454A
00404539     03BD D7010>add edi,dword ptr ss:[ebp+1D7]                   ; ss:[004045C7]=00D10000
0040453F     8B741A 14  mov esi,dword ptr ds:[edx+ebx+14]
00404543     03F2       add esi,edx
00404545     C1F9 02    sar ecx,2
00404548     F3:A5      rep movs dword ptr es:[edi],dword ptr ds:[esi]

前面都是在得到源地址esi和目标地址edi,循环计数器ecx这个值,关键的是最后的移动指令这一句,把0x5000双字的数据从003C0400移动到00D11000
总的是00D11000--00D25000

0040454A     83C3 28    add ebx,28
0040454D   ^ EB DA      jmp short ASP_Firs.00404529

跳回去再移动一次,这次是0x180双字的数据从003D4400移动到00D3B000,00D3B000--00D3B600
第三次,0x180双字的数据从003D4A00移动到00D3E000
第四次,0x100双字的数据从003D5000移动到00D3F000        ;这次都是00
第五次,0x800双字的数据从003D5400移动到00D40000
.。。。。不知道还有几次,我F4跳出循环了(OD的数据窗口要注意使用)。
把数据一块一块的移动到了后面分配的内存里

0040454F     8B85 D3010>mov eax,dword ptr ss:[ebp+1D3]                   ; ss:[004045C3]=003C0000
00404555     50         push eax
00404556     8B95 D7010>mov edx,dword ptr ss:[ebp+1D7]                   ; ss:[004045C7]=00D10000
0040455C     52         push edx                                                        ;把两块内存的地址压入堆栈
0040455D     8B18       mov ebx,dword ptr ds:[eax]                       ; 读出了【003C0000】
0040455F     03DA       add ebx,edx
00404561     8B85 EB030>mov eax,dword ptr ss:[ebp+3EB]                   ; ss:[004047DB]=7C80AC28 (kernel32.GetProcAddress)
00404567     8903       mov dword ptr ds:[ebx],eax       ; eax=7C80AC28 (kernel32.GetProcAddress)把地址存入了【00D438A4】
00404569     8B85 EF030>mov eax,dword ptr ss:[ebp+3EF]
0040456F     8943 04    mov dword ptr ds:[ebx+4],eax
00404572     8B85 F3030>mov eax,dword ptr ss:[ebp+3F3]
00404578     8943 08    mov dword ptr ds:[ebx+8],eax                     ; kernel32.LoadLibraryA

我们通过在OD数据窗口观察00D43870下面的数据,我们就会发现,这里就是壳的导入表的IMAGE_THUNK_DATA结构,下面则是IMAGE_IMPORT_BY_NAME结构,壳这样做实际上是在填充壳的这三个函数的IAT表,你可以提前用Peditor观察壳程序,看看它的导出表。
00D438A0  25 73 00 90 28 AC 80 7C 29 B5 80 7C 77 1D 80 7C  %s.?瑎|)祤|w_€|
00D438B0  00 00 00 00 6B 65 72 6E 65 6C 33 32 2E 64 6C 6C  ....kernel32.dll
00D438C0  00 00 00 47 65 74 50 72 6F 63 41 64 64 72 65 73  ...GetProcAddres
00D438D0  73 00 00 00 47 65 74 4D 6F 64 75 6C 65 48 61 6E  s...GetModuleHan
00D438E0  64 6C 65 41 00 00 00 4C 6F 61 64 4C 69 62 72 61  dleA...LoadLibra
00D438F0  72 79 41 00 00 00 00 00 00 00 00 00 00 00 00 00  ryA.............

0040457B     5F         pop edi
0040457C     5E         pop esi
0040457D     8B46 04    mov eax,dword ptr ds:[esi+4]                     ; 读出第一块内存的第二双字
00404580     03C7       add eax,edi
00404582     8985 CE010>mov dword ptr ss:[ebp+1CE],eax
00404588     8B55 5B    mov edx,dword ptr ss:[ebp+5B]                    ; ss:[0040444B]=00404048 (ASP_Firs.00404048)
0040458B     8B85 CE010>mov eax,dword ptr ss:[ebp+1CE]                   ; ss:[004045BE]=00D43000
00404591     8942 0C    mov dword ptr ds:[edx+C],eax
00404594     9B         wait

都是在读值,存值,继续

00404595     D9BD 3C040>fstcw word ptr ss:[ebp+43C]
0040459B     8D9D 14040>lea ebx,dword ptr ss:[ebp+414]                   ; 地址=00404804
004045A1     53         push ebx                         ; ebx=00404804 (ASP_Firs.00404804)
004045A2     6A 00      push 0
004045A4     6A 00      push 0
004045A6     6A 01      push 1
004045A8     57         push edi                         ; edi=00D10000
004045A9     8B5E 08    mov ebx,dword ptr ds:[esi+8]

压入了几个值,继续

004045AC     03DF       add ebx,edi
004045AE     53         push ebx
004045AF     68 0080000>push 8000
004045B4     6A 00      push 0
004045B6     56         push esi                                                        ;003C0000
004045B7     FF95 FB030>call dword ptr ss:[ebp+3FB]                      ; kernel32.VirtualFree

释放了第一块内存

004045BD     68 0030D40>push 0D43000
004045C2     C3         retn                                             ; 返回到上一条指令压入的地址

要去第二块内存运行了
我们来回想一下,先是把壳的数据搬到第一块内存,然后又从第一块移动到第二块,再跑到第二块执行程序

00D43000     90         nop
00D43001     60         pushad
00D43002     E8 4006000>call 00D43647

00D43647     8B2C24     mov ebp,dword ptr ss:[esp]
00D4364A     81ED 4B294>sub ebp,44294B                                ;ebp=009006BC
00D43650     C3         retn                                        ;返回到上一个call的返回地址00D43007

壳里面都是要用ebp作为一个定位数据的基址,这里先把ebp的值放好

00D43007    /EB 44      jmp short 00D4304D

00D4304D     BB 4429440>mov ebx,442944
00D43052     03DD       add ebx,ebp                                      ; ebx=00D43000,得到了执行代码的开始地址
00D43054     2B9D 71294>sub ebx,dword ptr ss:[ebp+442971]                ; ebx=00D10000,得到了分配的内存地址
00D4305A     83BD D8304>cmp dword ptr ss:[ebp+4430D8],0
00D43061     899D 2F2E4>mov dword ptr ss:[ebp+442E2F],ebx
00D43067     0F85 3E050>jnz 00D435AB
00D4306D     8D85 E0304>lea eax,dword ptr ss:[ebp+4430E0]                ; 地址=00D4379C, (ASCII "kernel32.dll")
00D43073     50         push eax
00D43074     FF95 EC314>call dword ptr ss:[ebp+4431EC]                   ; kernel32.GetModuleHandleA
00D4307A     8985 DC304>mov dword ptr ss:[ebp+4430DC],eax                ; 存到了00D43798

要获取Kernel32.dll的模块句柄,存到内存中
00D43790  08 00 00 00 00 00 00 00 00 00 80 7C 6B 65 72 6E  .........€|kern
00D437A0  65 6C 33 32 2E 64 6C 6C 00 56 69 72 74 75 61 6C  el32.dll.Virtual
00D437B0  41 6C 6C 6F 63 00 56 69 72 74 75 61 6C 46 72 65  Alloc.VirtualFre
00D437C0  65 00 56 69 72 74 75 61 6C 50 72 6F 74 65 63 74  e.VirtualProtect
00D437D0  00 45 78 69 74 50 72 6F 63 65 73 73 00 00 00 00  .ExitProcess....
00D437E0  00 75 73 65 72 33 32 2E 64 6C 6C 00 4D 65 73 73  .user32.dll.Mess
00D437F0  61 67 65 42 6F 78 41 00 77 73 70 72 69 6E 74 66  ageBoxA.wsprintf

00D43080     8BF8       mov edi,eax
00D43082     8D9D ED304>lea ebx,dword ptr ss:[ebp+4430ED]                ; 地址=00D437A9, (ASCII "VirtualAlloc")
00D43088     53         push ebx
00D43089     50         push eax
00D4308A     FF95 E8314>call dword ptr ss:[ebp+4431E8]                   ; kernel32.GetProcAddress
00D43090     8985 79294>mov dword ptr ss:[ebp+442979],eax                ; 存入00D43035

要获取VirtualAlloc函数

00D43096     8D9D FA304>lea ebx,dword ptr ss:[ebp+4430FA]                ; 地址=00D437B6, (ASCII "VirtualFree")
00D4309C     53         push ebx
00D4309D     57         push edi
00D4309E     FF95 E8314>call dword ptr ss:[ebp+4431E8]                   ; kernel32.GetProcAddress
00D430A4     8985 7D294>mov dword ptr ss:[ebp+44297D],eax                ; 存到00D43039

获得VirtualFree函数
把他们存到了连续的内存地址:
00D43030  00 00 00 00 00 81 9A 80 7C 14 9B 80 7C 00 00 00  .....仛€|_泙|...

00D430AA     8B85 2F2E4>mov eax,dword ptr ss:[ebp+442E2F]
00D430B0     8985 D8304>mov dword ptr ss:[ebp+4430D8],eax                ; eax=00D10000
00D430B6     6A 04      push 4
00D430B8     68 0010000>push 1000
00D430BD     68 4605000>push 546
00D430C2     6A 00      push 0
00D430C4     FF95 79294>call dword ptr ss:[ebp+442979]                   ; kernel32.VirtualAlloc
00D430CA     8985 75294>mov dword ptr ss:[ebp+442975],eax                ; eax=003C0000,存到00D43031

再次分配了一块内存,继续存进去:
00D43030  00 00 00 3C 00 81 9A 80 7C 14 9B 80 7C 00 00 00  ...<.仛€|_泙|...

00D430D0     8D9D 452A4>lea ebx,dword ptr ss:[ebp+442A45]
00D430D6     50         push eax                                                        ;新分配的内存地址
00D430D7     53         push ebx                                                        ;00D43101
00D430D8     E8 7405000>call 00D43651

带着新值,继续

00D43651     55         push ebp
00D43652     8BEC       mov ebp,esp
00D43654     60         pushad
00D43655     55         push ebp
00D43656     8B75 08    mov esi,dword ptr ss:[ebp+8]
00D43659     8B7D 0C    mov edi,dword ptr ss:[ebp+C]                     ; 分别取得call的两个参数

下面又是大段的运算,又要把数据从这里移动到新分配的003C0000
00D4365C     FC         cld
00D4365D     B2 80      mov dl,80
00D4365F     8A06       mov al,byte ptr ds:[esi]
00D43661     46         inc esi
00D43662     8807       mov byte ptr ds:[edi],al
00D43664     47         inc edi
00D43665     02D2       add dl,dl
00D43667     75 05      jnz short 00D4366E
00D43669     8A16       mov dl,byte ptr ds:[esi]
00D4366B     46         inc esi
00D4366C     12D2       adc dl,dl
00D4366E   ^ 73 EF      jnb short 00D4365F
00D43670     02D2       add dl,dl
00D43672     75 05      jnz short 00D43679
00D43674     8A16       mov dl,byte ptr ds:[esi]
00D43676     46         inc esi
00D43677     12D2       adc dl,dl
00D43679     73 4A      jnb short 00D436C5
00D4367B     33C0       xor eax,eax
00D4367D     02D2       add dl,dl
00D4367F     75 05      jnz short 00D43686
00D43681     8A16       mov dl,byte ptr ds:[esi]
00D43683     46         inc esi
00D43684     12D2       adc dl,dl
00D43686     0F83 D6000>jnb 00D43762
00D4368C     02D2       add dl,dl
00D4368E     75 05      jnz short 00D43695
00D43690     8A16       mov dl,byte ptr ds:[esi]
00D43692     46         inc esi
00D43693     12D2       adc dl,dl
00D43695     13C0       adc eax,eax
00D43697     02D2       add dl,dl
00D43699     75 05      jnz short 00D436A0
00D4369B     8A16       mov dl,byte ptr ds:[esi]
00D4369D     46         inc esi
00D4369E     12D2       adc dl,dl
00D436A0     13C0       adc eax,eax
00D436A2     02D2       add dl,dl
00D436A4     75 05      jnz short 00D436AB
00D436A6     8A16       mov dl,byte ptr ds:[esi]
00D436A8     46         inc esi
00D436A9     12D2       adc dl,dl
00D436AB     13C0       adc eax,eax
00D436AD     02D2       add dl,dl
00D436AF     75 05      jnz short 00D436B6
00D436B1     8A16       mov dl,byte ptr ds:[esi]
00D436B3     46         inc esi
00D436B4     12D2       adc dl,dl
00D436B6     13C0       adc eax,eax
00D436B8     74 06      je short 00D436C0
00D436BA     57         push edi
00D436BB     2BF8       sub edi,eax
00D436BD     8A07       mov al,byte ptr ds:[edi]
00D436BF     5F         pop edi
00D436C0     8807       mov byte ptr ds:[edi],al
00D436C2     47         inc edi
00D436C3   ^ EB A0      jmp short 00D43665
00D436C5     B8 0100000>mov eax,1
00D436CA     02D2       add dl,dl
00D436CC     75 05      jnz short 00D436D3
00D436CE     8A16       mov dl,byte ptr ds:[esi]
00D436D0     46         inc esi
00D436D1     12D2       adc dl,dl
00D436D3     13C0       adc eax,eax
00D436D5     02D2       add dl,dl
00D436D7     75 05      jnz short 00D436DE
00D436D9     8A16       mov dl,byte ptr ds:[esi]
00D436DB     46         inc esi
00D436DC     12D2       adc dl,dl
00D436DE   ^ 72 EA      jb short 00D436CA
00D436E0     83E8 02    sub eax,2
00D436E3     75 28      jnz short 00D4370D
00D436E5     B9 0100000>mov ecx,1
00D436EA     02D2       add dl,dl
00D436EC     75 05      jnz short 00D436F3
00D436EE     8A16       mov dl,byte ptr ds:[esi]
00D436F0     46         inc esi
00D436F1     12D2       adc dl,dl
00D436F3     13C9       adc ecx,ecx
00D436F5     02D2       add dl,dl
00D436F7     75 05      jnz short 00D436FE
00D436F9     8A16       mov dl,byte ptr ds:[esi]
00D436FB     46         inc esi
00D436FC     12D2       adc dl,dl
00D436FE   ^ 72 EA      jb short 00D436EA
00D43700     56         push esi
00D43701     8BF7       mov esi,edi
00D43703     2BF5       sub esi,ebp
00D43705     F3:A4      rep movs byte ptr es:[edi],byte ptr ds:[esi]
00D43707     5E         pop esi
00D43708   ^ E9 58FFFFF>jmp 00D43665
00D4370D     48         dec eax
00D4370E     C1E0 08    shl eax,8
00D43711     8A06       mov al,byte ptr ds:[esi]
00D43713     46         inc esi
00D43714     8BE8       mov ebp,eax
00D43716     B9 0100000>mov ecx,1
00D4371B     02D2       add dl,dl
00D4371D     75 05      jnz short 00D43724
00D4371F     8A16       mov dl,byte ptr ds:[esi]
00D43721     46         inc esi
00D43722     12D2       adc dl,dl
00D43724     13C9       adc ecx,ecx
00D43726     02D2       add dl,dl
00D43728     75 05      jnz short 00D4372F
00D4372A     8A16       mov dl,byte ptr ds:[esi]
00D4372C     46         inc esi
00D4372D     12D2       adc dl,dl
00D4372F   ^ 72 EA      jb short 00D4371B
00D43731     3D 007D000>cmp eax,7D00
00D43736     73 1A      jnb short 00D43752
00D43738     3D 0005000>cmp eax,500
00D4373D     72 0E      jb short 00D4374D
00D4373F     41         inc ecx
00D43740     56         push esi
00D43741     8BF7       mov esi,edi
00D43743     2BF0       sub esi,eax
00D43745     F3:A4      rep movs byte ptr es:[edi],byte ptr ds:[esi]
00D43747     5E         pop esi
00D43748   ^ E9 18FFFFF>jmp 00D43665
00D4374D     83F8 7F    cmp eax,7F
00D43750     77 03      ja short 00D43755
00D43752     83C1 02    add ecx,2
00D43755     56         push esi
00D43756     8BF7       mov esi,edi
00D43758     2BF0       sub esi,eax
00D4375A     F3:A4      rep movs byte ptr es:[edi],byte ptr ds:[esi]
00D4375C     5E         pop esi
00D4375D   ^ E9 03FFFFF>jmp 00D43665
00D43762     8A06       mov al,byte ptr ds:[esi]
00D43764     46         inc esi
00D43765     33C9       xor ecx,ecx
00D43767     C0E8 01    shr al,1
00D4376A     74 12      je short 00D4377E
00D4376C     83D1 02    adc ecx,2
00D4376F     8BE8       mov ebp,eax
00D43771     56         push esi
00D43772     8BF7       mov esi,edi
00D43774     2BF0       sub esi,eax
00D43776     F3:A4      rep movs byte ptr es:[edi],byte ptr ds:[esi]
00D43778     5E         pop esi
00D43779   ^ E9 E7FEFFF>jmp 00D43665

F4到下一行,跳出循环,看到edi=003C0546,可见移过来的数据并不多,这个call前面我们就已经进去过了。

00D4377E     5D         pop ebp
00D4377F     2B7D 0C    sub edi,dword ptr ss:[ebp+C]
00D43782     897D FC    mov dword ptr ss:[ebp-4],edi                     ; edi=00000546,得到总的移过来的数目
00D43785     61         popad                                            ; 上面存着的值,执行这条指令后,就存放在eax中了
00D43786     5D         pop ebp
00D43787     C2 0800    retn                                                         ;返回到00D430DD

把数量作为返回值

00D430DD     8BC8       mov ecx,eax
00D430DF     8DBD 452A4>lea edi,dword ptr ss:[ebp+442A45]
00D430E5     8BB5 75294>mov esi,dword ptr ss:[ebp+442975]
00D430EB     F3:A4      rep movs byte ptr es:[edi],byte ptr ds:[esi]     ; 把这0x546的数据又从003C0000移动回到了00D43101,就是改变我们下面一点的代码了

用里面的数据改变下面的代码,是自修改

00D430ED     8B85 75294>mov eax,dword ptr ss:[ebp+442975]
00D430F3     68 0080000>push 8000
00D430F8     6A 00      push 0
00D430FA     50         push eax                                                        ;003C0000
00D430FB     FF95 7D294>call dword ptr ss:[ebp+44297D]                   ; kernel32.VirtualFree
00D43101     8D85 512C4>lea eax,dword ptr ss:[ebp+442C51]                ; 地址=00D4330D
00D43107     50         push eax
00D43108     C3         retn                                             ; 返回到上面的地址

释放了申请的内存,原来又申请的内存只是一个中转,用来自修改代码,完了就释放,然后就返回了

00D4330D     8B9D 552A4>mov ebx,dword ptr ss:[ebp+442A55]
00D43313     0BDB       or ebx,ebx
00D43315     74 0A      je short 00D43321
00D43321     8DB5 712A4>lea esi,dword ptr ss:[ebp+442A71]
00D43327     833E 00    cmp dword ptr ds:[esi],0                         ; ds:[00D4312D]=00001000
00D4332A     0F84 D3000>je 00D43403
00D43330     8DB5 712A4>lea esi,dword ptr ss:[ebp+442A71]
00D43336     8B46 04    mov eax,dword ptr ds:[esi+4]
00D43339     6A 04      push 4
00D4333B     68 0010000>push 1000
00D43340     50         push eax
00D43341     6A 00      push 0
00D43343     FF95 79294>call dword ptr ss:[ebp+442979]                   ; kernel32.VirtualAlloc
00D43349     8985 75294>mov dword ptr ss:[ebp+442975],eax                ; 存内存基址=003C0000

又申请
00D43030  00 00 00 3C 00 81 9A 80 7C 14 9B 80 7C 00 00 00  ...<.仛€|_泙|...
这四个内存地址就是用来存取分配的内存地址和VirtualAlloc和VirtualFree两个函数

00D4334F     56         push esi
00D43350     8B1E       mov ebx,dword ptr ds:[esi]
00D43352     039D D8304>add ebx,dword ptr ss:[ebp+4430D8]
00D43358     50         push eax                                                        ;003C0000
00D43359     53         push ebx                                                        ;00D11000
00D4335A     E8 F202000>call 00D43651                                                ;这个call就是前面移动数据的哪一个了

进入call后,程序又把数据计算后移动到了003C0000,只是这次比较多0x29C00

00D4335F     80BD 70294>cmp byte ptr ss:[ebp+442970],0
00D43366     75 4C      jnz short 00D433B4
00D43368     FE85 70294>inc byte ptr ss:[ebp+442970]          ; 上面把里面的值比0,这里又把它加1,貌似这是一个标志位00D4302C
00D4336E     8B3E       mov edi,dword ptr ds:[esi]
00D43370     03BD D8304>add edi,dword ptr ss:[ebp+4430D8]                ; edi=00D11000
00D43376     FF37       push dword ptr ds:[edi]                          ; ds:[00D11000]=40103804
00D43378     C607 C3    mov byte ptr ds:[edi],0C3
00D4337B     FFD7       call edi                                         ; edi=00D11000

这里是要跳到00D11000出执行,先把开头的第一个字节改为C3,我们来看看C3原来就是retn指令,继续

00D11000     C3         retn

只有一条返回指令返回到00D4337D

00D4337D     8F07       pop dword ptr ds:[edi]
00D4337F     50         push eax
00D43380     51         push ecx
00D43381     56         push esi
00D43382     53         push ebx
00D43383     8BC8       mov ecx,eax
00D43385     83E9 06    sub ecx,6
00D43388     8BB5 75294>mov esi,dword ptr ss:[ebp+442975]
00D4338E     33DB       xor ebx,ebx

保存了寄存器后,又给寄存器赋值:
EAX 00029C00
ECX 00029BFA
EDX 7C92EB94 ntdll.KiFastSystemCallRet
EBX 00000000
ESP 0012FF58
EBP 009006BC
ESI 003C0000
EDI 00D11000
EIP 00D43396

00D43390     0BC9       or ecx,ecx
00D43392    /74 1C      je short 00D433B0
00D43394    |78 1A      js short 00D433B0                        ;ecx是循环计数器,看看是不是为零了,是就跳出循环。
00D43396    |AC         lods byte ptr ds:[esi]                ;读入字节
00D43397    |3C E8      cmp al,0E8
00D43399    |74 08      je short 00D433A3                        ;等于E8吗?
00D4339B    |3C E9      cmp al,0E9
00D4339D    |74 04      je short 00D433A3                        ;等于E9吗?
00D4339F    |43         inc ebx                                ;这里则是记录已经比了几个还没有找到
00D433A0    |49         dec ecx
00D433A1   ^|EB ED      jmp short 00D43390

这段就是从003C0000处按字节依次读入数据,然后比较看看是不是E8和E9,不是就把ebx加1,把ecx总循环的计数器减1,然后继续读,也就是说这段循环在003C0000位置寻找E8和E9

00D433A3     291E       sub dword ptr ds:[esi],ebx            ;ebx=E8

F4下来,找到的一个在【003C01CA】=E8
找到后就把他后面的四个字节用ebx的值填充,现在我们来想一下壳到底在做什么?我们来看看把数据窗口d 003C0000,鼠标找到003C01CA,也就是循环里面找到的第一个E8,然后右键选择反汇编,我们发现了,原来E8就是call 指令的机器码(实际上call有好多种,机器码也不同,细节你可以去看intel的Intel Architecture Software Developer Manual,第二卷就可以找到了,这里我们为了简化):
003C01C4    BB 0A000000     mov ebx,0A
003C01C9    54              push esp
003C01CA    E8 23010000     call 003C02F2                                ;这一句
003C01CF    F64424 2C 01    test byte ptr ss:[esp+2C],1
003C01D4    74 05           je short 003C01DB
找到E8后,00D433A3这一句修改他后面的四个字节,实际上就是把E8 23010000的后面给改掉,这样的话,call的调用地址就改了,现在我们执行这一句指令,同时就可以看到数据窗口里,call的这一句已经变成:
003C01CA    E8 59FFFFFF     call 003C0128
E8是call指令,E9呢?原来是jmp指令
我们来算算他一共要改多少处,ecx=00029BFA,每次减去5,等于零跳出循环,那么00029BFA/5=8598,呵呵,还真是不少。

00D433A5     83C3 05    add ebx,5
00D433A8     83C6 04    add esi,4
00D433AB     83E9 05    sub ecx,5                             ; 把计数器,内存地址指针修改到相应的位置,继续循环
00D433AE   ^ EB E0      jmp short 00D43390

F4到下一行跳出循环,下面就是改完收工,重新pop出原来的值到寄存器

00D433B0    \5B         pop ebx
00D433B1     5E         pop esi
00D433B2     59         pop ecx
00D433B3     58         pop eax
00D433B4     8BC8       mov ecx,eax
00D433B6     8B3E       mov edi,dword ptr ds:[esi]
00D433B8     03BD D8304>add edi,dword ptr ss:[ebp+4430D8]
00D433BE     8BB5 75294>mov esi,dword ptr ss:[ebp+442975]
00D433C4     C1F9 02    sar ecx,2
00D433C7     F3:A5      rep movs dword ptr es:[edi],dword ptr ds:[esi]

把那些修改了call和jmp指令的数据按照每次四字节,从003C0000移动到00D11000,大小0x0000A700

00D433C9     8BC8       mov ecx,eax                           ; eax就是给ecx赋值的=00029C00
00D433CB     83E1 03    and ecx,3                             ; and后ecx等于零
00D433CE     F3:A4      rep movs byte ptr es:[edi],byte ptr d>;由于ecx上面and后等于0,所以一个字节也没有移动
00D433D0     5E         pop esi                               ; 00D4312D
00D433D1     8B85 75294>mov eax,dword ptr ss:[ebp+442975]
00D433D7     68 0080000>push 8000
00D433DC     6A 00      push 0
00D433DE     50         push eax
00D433DF     FF95 7D294>call dword ptr ss:[ebp+44297D]                   ; kernel32.VirtualFree
00D433E5     83C6 08    add esi,8
00D433E8     833E 00    cmp dword ptr ds:[esi],0
00D433EB   ^ 0F85 45FFF>jnz 00D43336                                                ;循环来了

现在又把它释放掉,循环跳回去到分配内存处,我们来想一下这个循环的作用,它就是分配一块内存,把数据计算了放进去,然后又把数据放回来释放,中间的话可能会去执行修改call的循环,我们跟着他回去过一下。在这里就是判断是否去执行修改call循环,现在我们发现由于第一次给它赋了1,这个比较都是会跳转的。那个循环只会去执行一次。
00D4335F     80BD 70294>cmp byte ptr ss:[ebp+442970],0
00D43366     75 4C      jnz short 00D433B4
00D43368     FE85 70294>inc byte ptr ss:[ebp+442970]          ; 上面把里面的值比0,这里又把它加1,貌似这是一个标志位00D4302C
F4跳出循环

00D433F1     8B9D 552A4>mov ebx,dword ptr ss:[ebp+442A55]
00D433F7     0BDB       or ebx,ebx
00D433F9     74 08      je short 00D43403                     ; 读取一个值,判断是否为零
00D433FB     8B03       mov eax,dword ptr ds:[ebx]
00D433FD     8785 592A4>xchg dword ptr ss:[ebp+442A59],eax
00D43403     8B95 D8304>mov edx,dword ptr ss:[ebp+4430D8]     ; ss:[00D43794]=00D10000

我们一定要利用好OD的数据窗口,要经常的在提示栏的数据上点右键,选择在数据窗口跟随地址。这里我们同样跟随一下就会发现,读取的地址数据周围有很重要的字符,我们修正一下显示的对齐方式,d 00D43700得到像下面的数据是这样的:
00D43780  7D 0C 89 7D FC 61 5D C2 08 00 87 DB 00 00 00 00  }.墋黙]?.囒....
00D43790  08 00 00 00 00 00 D1 00 00 00 80 7C 6B 65 72 6E  .....?..€|kern
00D437A0  65 6C 33 32 2E 64 6C 6C 00 56 69 72 74 75 61 6C  el32.dll.Virtual
00D437B0  41 6C 6C 6F 63 00 56 69 72 74 75 61 6C 46 72 65  Alloc.VirtualFre
00D437C0  65 00 56 69 72 74 75 61 6C 50 72 6F 74 65 63 74  e.VirtualProtect
00D437D0  00 45 78 69 74 50 72 6F 63 65 73 73 00 00 00 00  .ExitProcess....
00D437E0  00 75 73 65 72 33 32 2E 64 6C 6C 00 4D 65 73 73  .user32.dll.Mess
00D437F0  61 67 65 42 6F 78 41 00 77 73 70 72 69 6E 74 66  ageBoxA.wsprintf
00D43800  41 00 4C 4F 41 44 45 52 20 45 52 52 4F 52 00 54  A.LOADER ERROR.T
你也应该看到了,这可是重要的地方了,我们要小心的继续

00D43409     8B85 512A4>mov eax,dword ptr ss:[ebp+442A51]
00D4340F     2BD0       sub edx,eax                           ; edx=00910000
00D43411     74 75      je short 00D43488
00D43413     8BC2       mov eax,edx
00D43415     C1E8 10    shr eax,10                            ; eax=0000091
00D43418     33DB       xor ebx,ebx
00D4341A     8BB5 5D2A4>mov esi,dword ptr ss:[ebp+442A5D]
00D43420     03B5 D8304>add esi,dword ptr ss:[ebp+4430D8]     ; esi=00D40000
00D43426     833E 00    cmp dword ptr ds:[esi],0              ; ds:[00D40000]=00001000
00D43429     74 5D      je short 00D43488
00D4342B     8B4E 04    mov ecx,dword ptr ds:[esi+4]

连续使用了esi内连续内存的数值,我们来看一下:
00D40000  00 10 00 00 48 01 00 00 00 30 0C 30 2C 30 38 30  ._..H_...0.0,080
00D40010  3C 30 40 30 44 30 48 30 4C 30 50 30 54 30 62 30  <0@0D0H0L0P0T0b0

00D4342E     83E9 08    sub ecx,8
00D43431     D1E9       shr ecx,1                             ; ecx=000000A0这是重定位项的数目,把它作为循环的计数器
00D43433     8B3E       mov edi,dword ptr ds:[esi]
00D43435     03BD D8304>add edi,dword ptr ss:[ebp+4430D8]     ; edi=00D11000,得到当前代码的虚拟地址
00D4343B     83C6 08    add esi,8                             ; esi=00D40008,从这里开始就是重定位项了
00D4343E     66:8B1E    mov bx,word ptr ds:[esi]              ; 读出重定位的项目
00D43441     C1EB 0C    shr ebx,0C
00D43444     83FB 01    cmp ebx,1                             ; 单独取出高4位进行判断
00D43447     74 0C      je short 00D43455
00D43449     83FB 02    cmp ebx,2
00D4344C     74 16      je short 00D43464
00D4344E     83FB 03    cmp ebx,3
00D43451     74 20      je short 00D43473                     ; 判断出重定位项的类型,一般都是3类
00D43453     EB 2C      jmp short 00D43481
00D43455     66:8B1E    mov bx,word ptr ds:[esi]
00D43458     81E3 FF0F0>and ebx,0FFF
00D4345E     66:01041F  add word ptr ds:[edi+ebx],ax
00D43462     EB 1D      jmp short 00D43481
00D43464     66:8B1E    mov bx,word ptr ds:[esi]
00D43467     81E3 FF0F0>and ebx,0FFF
00D4346D     66:01141F  add word ptr ds:[edi+ebx],dx
00D43471     EB 0E      jmp short 00D43481
00D43473     66:8B1E    mov bx,word ptr ds:[esi]
00D43476     81E3 FF0F0>and ebx,0FFF                          ; 得到重定位数据在页面中的地址
00D4347C     01141F     add dword ptr ds:[edi+ebx],edx        ; 给这个地址加上00910000,重定位过来
00D4347F     EB 00      jmp short 00D43481
00D43481     83C6 02    add esi,2                             ; 修改指向下一个字,重定位项是按照字的
00D43484   ^ E2 B8      loopd short 00D4343E                  ; 还没有结束,好回去继续
00D43486   ^\EB 9E      jmp short 00D43426                    ; 一个循环只是完成了一块的数据,回去看看是不是已经重定位完了

00D40000  00 10 00 00 48 01 00 00 00 30 0C 30 2C 30 38 30  ._..H_...0.0,080
00D40010  3C 30 40 30 44 30 48 30 4C 30 50 30 54 30 62 30  <0@0D0H0L0P0T0b0
00D40020  6A 30 72 30 7A 30 82 30 8A 30 92 30 9A 30 A2 30  j0r0z0?????
00D40030  AA 30 B2 30 BA 30 C2 30 CA 30 D2 30 DA 30 E2 30  ????????
00D40040  EA 30 F2 30 FA 30 02 31 0A 31 12 31 1A 31 22 31  ???_1.1_1_1"1
00D40050  2A 31 32 31 3A 31 42 31 4A 31 52 31 5A 31 62 31  *121:1B1J1R1Z1b1
00D40060  6A 31 72 31 7A 31 82 31 8A 31 92 31 9A 31 A2 31  j1r1z1?????
00D40070  AA 31 B2 31 BA 31 E6 31 EE 31 F6 31 FE 31 06 32  ???????_2
00D40080  0E 32 16 32 1E 32 27 32 48 32 50 32 B8 32 BF 32  _2_2_2'2H2P2??
由于壳自己把数据从程序空间搬到了动态内存里面,上面的就是重定位表,壳在进行代码的重定位,还是一样的话,不熟悉PE文件结构的,我也没有办法,赶快去补课吧!
完成一个块后,跳回去又是下一个块直到结束;列一下他们的IMAGE_BASE_RELOCATION结构
00D40000  00 10 00 00 48 01 00 00                          ._..H_..
00D40140                          00 20 00 00 CC 00 00 00          ?..
00D40210              00 30 00 00 80 00 00 00                  0..€..._
00D40290              00 40 00 00 68 00 00 00                  @..h...?
00D402F0                                      00 50 00 00 54 02 00 00                                      
00D40550  00 60 00 00 FC 01 00 00                          .`..?...
00D40740                                      00 70 00 00 A4 00 00 00                                      
太多了,F4跳出来

00D43488     8BB5 612A4>mov esi,dword ptr ss:[ebp+442A61]     ; ss:[00D4311D]=0002E000
00D4348E     8B95 D8304>mov edx,dword ptr ss:[ebp+4430D8]     ; ss:[00D43794]=00D10000
00D43494     03F2       add esi,edx                           ; 这里就是定位到导入表了(当然我是从下面才判断出来的)
00D43496     8B46 0C    mov eax,dword ptr ds:[esi+C]          ; esi=00D3E000,取出它+C地方的值
00D43499     85C0       test eax,eax
00D4349B     0F84 0A010>je 00D435AB
00D434A1     03C2       add eax,edx
00D434A3     8BD8       mov ebx,eax                           ; eax=00D3E368, (ASCII "kernel32.dll")从这一句我们可以判断出了,我们来到导入表了

esi得到的导入表IMAGE_IMPORT_DESCRIPTOR结构,【esi+C】就是Name1字段了,得到库字符串名的RVA
加上基址,就得到了库的名字,这里是kernel32.dll库。

00D434A5     50         push eax
00D434A6     FF95 EC314>call dword ptr ss:[ebp+4431EC]                ;这是GetModuleHandleA函数
00D434AC     85C0       test eax,eax
00D434AE     75 07      jnz short 00D434B7

获取Kernel32.dll库的模块句柄

00D434B7     8985 4D294>mov dword ptr ss:[ebp+44294D],eax     ; 值保存到00D43009
00D434BD     C785 51294>mov dword ptr ss:[ebp+442951],0       ; 在存入地址的后面再放入一个字节的0
00D434C7     8B95 D8304>mov edx,dword ptr ss:[ebp+4430D8]
00D434CD     8B06       mov eax,dword ptr ds:[esi]            ; ds:[00D3E000]=这个是导入表的OriginalFirstThunk字段
00D434CF     85C0       test eax,eax                          ; 看看是不是零,一般都是零啥
00D434D1     75 03      jnz short 00D434D6
00D434D3     8B46 10    mov eax,dword ptr ds:[esi+10]         ; +10么就来到FirstThunk字段了
00D434D6     03C2       add eax,edx                           ; 加上基址,现在eax指向IMAGE_THUNK_DATA结构了
00D434D8     0385 51294>add eax,dword ptr ss:[ebp+442951]     ; 加上要定位的数组序号,先是0,从第一个开始
00D434DE     8B18       mov ebx,dword ptr ds:[eax]            ; 取出IMAGE_THUNK_DATA结构的值
00D434E0     8B7E 10    mov edi,dword ptr ds:[esi+10]         ; 再次让edi得到数组的RVA
00D434E3     03FA       add edi,edx
00D434E5     03BD 51294>add edi,dword ptr ss:[ebp+442951]     ; 重新定位数组在edi,这是要把得到的系统地址放到里面,构建IAT表
00D434EB     85DB       test ebx,ebx                          ; 测试是不是已经完了,
00D434ED     0F84 A2000>je 00D43595
00D434F3     F7C3 00000>test ebx,80000000                      ; 这是壳在获取自身的导出表,这里在比较IMAGE_THUNK_DATA的高位
00D434F9    /75 04      jnz short 00D434FF                    ; 没有跳,说明这是IMAGE_IMPORT_BY_NAME结构RVA
00D434FB    |03DA       add ebx,edx                           ; 加上基址,得到IMAGE_IMPORT_BY_NAME结构
00D434FD    |43         inc ebx
00D434FE    |43         inc ebx                               ; 加上2字节的位置,去掉IMAGE_IMPORT_BY_NAME的Hint字段
00D434FF    \53         push ebx                              ; 我们的第一个函数是GetCurrentThreadId
00D43500     81E3 FFFFF>and ebx,7FFFFFFF
00D43506     53         push ebx
00D43507     FFB5 4D294>push dword ptr ss:[ebp+44294D]
00D4350D     FF95 E8314>call dword ptr ss:[ebp+4431E8]        ; ss:[00D438A4]=7C80AC28 (kernel32.GetProcAddress)
00D43513     85C0       test eax,eax                          ; 测试是否成功
00D43515     5B         pop ebx
00D43516     75 6F      jnz short 00D43587

取得函数系统地址

00D43587     8907       mov dword ptr ds:[edi],eax            ; 存到IMAGE_THUNK_DATA数组,构建IAT表
00D43589     8385 51294>add dword ptr ss:[ebp+442951],4       ; 增加定位数组的序号
00D43590   ^ E9 32FFFFF>jmp 00D434C7

我们通过在数据窗口就可以看到要读取的函数数量非常多,我们F4跳出循环后,已经读取了如下的函数:
00D3E370                          47 65 74 43 75 72 72 65          GetCurre
00D3E380  6E 74 54 68 72 65 61 64 49 64 00 00 00 00 44 65  ntThreadId....De
00D3E390  6C 65 74 65 43 72 69 74 69 63 61 6C 53 65 63 74  leteCriticalSect
00D3E3A0  69 6F 6E 00 00 00 4C 65 61 76 65 43 72 69 74 69  ion...LeaveCriti
00D3E3B0  63 61 6C 53 65 63 74 69 6F 6E 00 00 00 00 45 6E  calSection....En
00D3E3C0  74 65 72 43 72 69 74 69 63 61 6C 53 65 63 74 69  terCriticalSecti
00D3E3D0  6F 6E 00 00 00 00 49 6E 69 74 69 61 6C 69 7A 65  on....Initialize
00D3E3E0  43 72 69 74 69 63 61 6C 53 65 63 74 69 6F 6E 00  CriticalSection.
00D3E3F0  00 00 56 69 72 74 75 61 6C 46 72 65 65 00 00 00  ..VirtualFree...
00D3E400  56 69 72 74 75 61 6C 41 6C 6C 6F 63 00 00 00 00  VirtualAlloc....
00D3E410  4C 6F 63 61 6C 46 72 65 65 00 00 00 4C 6F 63 61  LocalFree...Loca
00D3E420  6C 41 6C 6C 6F 63 00 00 00 00 56 69 72 74 75 61  lAlloc....Virtua
00D3E430  6C 51 75 65 72 79 00 00 00 00 57 69 64 65 43 68  lQuery....WideCh
00D3E440  61 72 54 6F 4D 75 6C 74 69 42 79 74 65 00 00 00  arToMultiByte...
00D3E450  4D 75 6C 74 69 42 79 74 65 54 6F 57 69 64 65 43  MultiByteToWideC
00D3E460  68 61 72 00 00 00 6C 73 74 72 6C 65 6E 41 00 00  har...lstrlenA..
00D3E470  00 00 6C 73 74 72 63 70 79 6E 41 00 00 00 6C 73  ..lstrcpynA...ls
00D3E480  74 72 63 70 79 41 00 00 00 00 4C 6F 61 64 4C 69  trcpyA....LoadLi
00D3E490  62 72 61 72 79 45 78 41 00 00 00 00 47 65 74 54  braryExA....GetT
00D3E4A0  68 72 65 61 64 4C 6F 63 61 6C 65 00 00 00 47 65  hreadLocale...Ge
00D3E4B0  74 53 74 61 72 74 75 70 49 6E 66 6F 41 00 00 00  tStartupInfoA...
00D3E4C0  47 65 74 50 72 6F 63 41 64 64 72 65 73 73 00 00  GetProcAddress..
00D3E4D0  00 00 47 65 74 4D 6F 64 75 6C 65 48 61 6E 64 6C  ..GetModuleHandl
00D3E4E0  65 41 00 00 00 00 47 65 74 4D 6F 64 75 6C 65 46  eA....GetModuleF
00D3E4F0  69 6C 65 4E 61 6D 65 41 00 00 00 00 47 65 74 4C  ileNameA....GetL
00D3E500  6F 63 61 6C 65 49 6E 66 6F 41 00 00 00 00 47 65  ocaleInfoA....Ge
00D3E510  74 4C 61 73 74 45 72 72 6F 72 00 00 00 00 47 65  tLastError....Ge
00D3E520  74 43 6F 6D 6D 61 6E 64 4C 69 6E 65 41 00 00 00  tCommandLineA...
00D3E530  46 72 65 65 4C 69 62 72 61 72 79 00 00 00 46 69  FreeLibrary...Fi
00D3E540  6E 64 46 69 72 73 74 46 69 6C 65 41 00 00 00 00  ndFirstFileA....
00D3E550  46 69 6E 64 43 6C 6F 73 65 00 00 00 45 78 69 74  FindClose...Exit
00D3E560  50 72 6F 63 65 73 73 00 00 00 57 72 69 74 65 46  Process...WriteF
00D3E570  69 6C 65 00 00 00 55 6E 68 61 6E 64 6C 65 64 45  ile...UnhandledE
00D3E580  78 63 65 70 74 69 6F 6E 46 69 6C 74 65 72 00 00  xceptionFilter..
00D3E590  00 00 53 65 74 46 69 6C 65 50 6F 69 6E 74 65 72  ..SetFilePointer
00D3E5A0  00 00 00 00 53 65 74 45 6E 64 4F 66 46 69 6C 65  ....SetEndOfFile
00D3E5B0  00 00 00 00 52 74 6C 55 6E 77 69 6E 64 00 00 00  ....RtlUnwind...
00D3E5C0  52 65 61 64 46 69 6C 65 00 00 00 00 52 61 69 73  ReadFile....Rais
00D3E5D0  65 45 78 63 65 70 74 69 6F 6E 00 00 00 00 47 65  eException....Ge
00D3E5E0  74 53 74 64 48 61 6E 64 6C 65 00 00 00 00 47 65  tStdHandle....Ge
00D3E5F0  74 46 69 6C 65 53 69 7A 65 00 00 00 47 65 74 53  tFileSize...GetS
00D3E600  79 73 74 65 6D 54 69 6D 65 00 00 00 47 65 74 46  ystemTime...GetF
00D3E610  69 6C 65 54 79 70 65 00 00 00 43 72 65 61 74 65  ileType...Create
00D3E620  46 69 6C 65 41 00 00 00 43 6C 6F 73 65 48 61 6E  FileA...CloseHan
00D3E630  64 6C 65                                         dle

00D43595     8906       mov dword ptr ds:[esi],eax            ; eax=00D3E1A8,这是循环写入Kernel32.dll所有函数地址的保存内存地址
00D43597     8946 0C    mov dword ptr ds:[esi+C],eax          ; Name1字段也同样的写入这个值
00D4359A     8946 10    mov dword ptr ds:[esi+10],eax         ; FirstThunk字段也是一样的了
00D4359D     83C6 14    add esi,14                            ; 指向下一个IID表
00D435A0     8B95 D8304>mov edx,dword ptr ss:[ebp+4430D8]     ; 得到基址
00D435A6   ^ E9 EBFEFFF>jmp 00D43496

把最后的地址存到三个字段后,定位到下一个IID表,跳转回去要看看是不是可以还有其他连接库
第二次是User32.dll库,获取了如下的函数:存到00D3E1AC
00D3E640  00 00 47 65 74 4B 65 79 62 6F 61 72 64 54 79 70  ..GetKeyboardTyp
00D3E650  65 00 00 00 4C 6F 61 64 53 74 72 69 6E 67 41 00  e...LoadStringA.
00D3E660  00 00 4D 65 73 73 61 67 65 42 6F 78 41 00 00 00  ..MessageBoxA...
00D3E670  43 68 61 72 4E 65 78 74 41 00                    CharNextA.
第三次是Advapi32.dll库,获取了如下的函数:存到了00D3E1C0
00D3E680                                52 65 67 51 75 65            RegQue
00D3E690  72 79 56 61 6C 75 65 45 78 41 00 00 00 00 52 65  ryValueExA....Re
00D3E6A0  67 4F 70 65 6E 4B 65 79 45 78 41 00 00 00 52 65  gOpenKeyExA...Re
00D3E6B0  67 43 6C 6F 73 65 4B 65 79 00                    gCloseKey.
接下来还有很多的库和函数,我们不再列举,F4跳出循环
edi最后指向了00D3E364

00D435AB     8B85 652A4400 mov eax,dword ptr ss:[ebp+442A65]     ; ss:[00D43121]=0002A8E8
00D435B1     50            push eax
00D435B2     0385 D8304400 add eax,dword ptr ss:[ebp+4430D8]     ; 把RVA+基址得到了00D3A8E8
00D435B8     5B            pop ebx
00D435B9     0BDB          or ebx,ebx
00D435BB     8985 112F4400 mov dword ptr ss:[ebp+442F11],eax     ; 把eax=00D3A8E8放到里面更改了里面的代码,这句代码就在我们的下面
00D435C1     61            popad
00D435C2     75 08         jnz short 00D435CC

00D435CC     68 E8A8D300   push 0D3A8E8                          ; 这句就是上面修改的代码
00D435D1     C3            retn

修改了一个call地址,然后跳转到哪里执行

00D3A8E8     55           push ebp
00D3A8E9     8BEC         mov ebp,esp
00D3A8EB     83C4 B4      add esp,-4C                    ; 0x4C的局部变量
00D3A8EE     B8 C8A6D300  mov eax,0D3A6C8                ; 给eax赋常量0D3A6C8
00D3A8F3     E8 E0AEFDFF  call 00D157D8                                ;进入
00D3A8F8     E8 9F8CFDFF  call 00D1359C
00D3A8FD     8D40 00      lea eax,dword ptr ds:[eax]

00D157D8     BA A4B0D300  mov edx,0D3B0A4
00D157DD     837D 0C 01   cmp dword ptr ss:[ebp+C],1     ; 读出了第二个参数比较是否为零
00D157E1     75 2A        jnz short 00D1580D
00D157E3     50           push eax
00D157E4     52           push edx                       ; 把新得到的两个常量保存起来
00D157E5     C605 C8C4D30>mov byte ptr ds:[D3C4C8],1     ; ds:[00D3C4C8]=00
00D157EC     8B4D 08      mov ecx,dword ptr ss:[ebp+8]   ; 读出第一个参数00D10000(不知道是在哪里压入的参数了!!)
00D157EF     890D D4C4D30>mov dword ptr ds:[D3C4D4],ecx
00D157F5     894A 04      mov dword ptr ds:[edx+4],ecx                ;00D3B0A8
00D157F8     C742 08 0000>mov dword ptr ds:[edx+8],0
00D157FF     C742 0C 0000>mov dword ptr ds:[edx+C],0
00D15806     E8 81FFFFFF  call 00D1578C

这里只是在内存中保存了几个数,还不知道是要做什么的,我们继续

00D1578C     53           push ebx
00D1578D     81C4 F8FEFFF>add esp,-108                   ; 分配0x108的局部变量
00D15793     68 05010000  push 105                       ; esp=0012FE1C
00D15798     8D4424 04    lea eax,dword ptr ss:[esp+4]
00D1579C     50           push eax                       ; eax=0012FE20
00D1579D     A1 D4C4D300  mov eax,dword ptr ds:[D3C4D4]  ; ecx=00D10000 这个是前面一个call ecx压入的值
00D157A2     50           push eax
00D157A3     E8 A4FEFFFF  call 00D1564C                  ; jmp to kernel32.GetModuleFileNameA

调用函数了:
0012FE14   00D10000  |hModule = 00D10000
0012FE18   0012FE20  |PathBuffer = 0012FE20
0012FE1C   00000105  \BufSize = 105 (261.)
我们要注意到它把Buffer设在了堆栈里,更要注意的是D10000本身就是函数自己请求的动态内存,这个函数返回0
而且我们注意到程序并没有检测返回值,而是要进入另一个call

00D157A8     8BC4         mov eax,esp                    ; esp=0012FE20
00D157AA     E8 B5EEFFFF  call 00D14664

00D14664     55           push ebp
00D14665     8BEC         mov ebp,esp
00D14667     81C4 E0FEFFF>add esp,-120                   ; 再次分配了0x120的局部变量

00D1466D     53           push ebx
00D1466E     56           push esi                        ; 压入了ebx=00D2E5B4,esi=003C0000
00D1466F     8945 FC      mov dword ptr ss:[ebp-4],eax    ; eax的值是从调用处来的,移动到【ebp-4】,就是第一个局部变量
00D14672     68 05010000  push 105
00D14677     8D85 E3FEFFF>lea eax,dword ptr ss:[ebp-11D]  ; 堆栈地址=0012FCFB,我们要注意到这个堆栈地址,它并不是标准的对齐方式
00D1467D     50           push eax
00D1467E     6A 00        push 0
00D14680     E8 8BCAFFFF  call 00D11110                   ; jmp to kernel32.GetModuleFileNameA

0012FCE4   00000000  |hModule = NULL
0012FCE8   0012FCFB  |PathBuffer = 0012FCFB
0012FCEC   00000105  \BufSize = 105 (261.)
还是调用这个函数,这次的hModule是NULL得到了当前程序的完整路径,

00D14685     C645 EE 00   mov byte ptr ss:[ebp-12],0
00D14689     8D45 F8      lea eax,dword ptr ss:[ebp-8]    ; eax=0012FE10
00D1468C     50           push eax
00D1468D     68 3F000F00  push 0F003F
00D14692     6A 00        push 0
00D14694     68 2048D100  push 0D14820                    ; ASCII "Software\Borland\Locales"
00D14699     68 01000080  push 80000001
00D1469E     E8 CDCAFFFF  call 00D11170                   ; jmp to ADVAPI32.RegOpenKeyExA

打开了指定的注册表的值
0012FCDC   80000001  |hKey = HKEY_CURRENT_USER
0012FCE0   00D14820  |Subkey = "Software\Borland\Locales"
0012FCE4   00000000  |Reserved = 0
0012FCE8   000F003F  |Access = KEY_ALL_ACCESS
0012FCEC   0012FE10  \pHandle = 0012FE10
我的电脑里根本就没有它要打开的这个注册表项。

00D146A3     85C0         test eax,eax
00D146A5     74 1E        je short 00D146C5                        ;没有办法跳,我的这里是失败的
00D146A7     8D45 F8      lea eax,dword ptr ss:[ebp-8]
00D146AA     50           push eax
00D146AB     68 3F000F00  push 0F003F
00D146B0     6A 00        push 0
00D146B2     68 3C48D100  push 0D1483C                    ; ASCII "Software\Borland\Delphi\Locales"
00D146B7     68 01000080  push 80000001
00D146BC     E8 AFCAFFFF  call 00D11170                   ; jmp to ADVAPI32.RegOpenKeyExA
00D146C1     85C0         test eax,eax
00D146C3     75 72        jnz short 00D14737                        ;这里肯定是跳了

再来一次看看:
0012FCDC   80000001  |hKey = HKEY_CURRENT_USER
0012FCE0   00D1483C  |Subkey = "Software\Borland\Delphi\Locales"
0012FCE4   00000000  |Reserved = 0
0012FCE8   000F003F  |Access = KEY_ALL_ACCESS
0012FCEC   0012FE10  \pHandle = 0012FE10
不用说在我这里还是没有,我的电脑里没有关于Borland的任何软件。由于我的没有,我也就不再去跟踪程序怎么处理有的这一类情况了,继续

00D14737     8B45 FC      mov eax,dword ptr ss:[ebp-4]
00D1473A     50           push eax
00D1473B     8D85 E3FEFFF>lea eax,dword ptr ss:[ebp-11D]
00D14741     50           push eax
00D14742     E8 01CAFFFF  call 00D11148                   ; jmp to kernel32.lstrcpyA

堆栈如下:
0012FCE8   0012FCFB  |String1 = 0012FCFB
0012FCEC   0012FE20  \String2 = "ASCII "F:\Unpack\goal\ASP_FirstWindow1.4.exe"
把这个call得到的文件路径,复制到前一个call的堆栈。
0012FCF0                                   00 3A 5C 55 6E             :\Un
0012FD00  70 61 63 6B 5C 67 6F 61 6C 5C 41 53 50 5F 46 69  pack\goal\ASP_Fi
0012FD10  72 73 74 57 69 6E 64 6F 77 31 2E 34 2E 65 78 65  rstWindow1.4.exe
0012FD20  00                                               .

00D14747     6A 05        push 5
00D14749     8D45 F3      lea eax,dword ptr ss:[ebp-D]
00D1474C     50           push eax
00D1474D     6A 03        push 3
00D1474F     E8 DCC9FFFF  call 00D11130                   ; jmp to kernel32.GetThreadLocale

调用了The GetThreadLocale function returns the calling thread's current locale.
返回值是00000804,同时这是一个没有参数的函数,所以前面压入堆栈的值,并不是给这个函数的。

00D14754     50           push eax
00D14755     E8 AEC9FFFF  call 00D11108                   ; jmp to kernel32.GetLocaleInfoA

前面的堆栈数是这个函数的参数如下:
0012FCE0   00000804  |LocaleId = 804
0012FCE4   00000003  |InfoType = 3
0012FCE8   0012FE0B  |Buffer = 0012FE0B
0012FCEC   00000005  \BufSize = 5
The GetLocaleInfo function retrieves information about a locale.
返回值是4,堆栈中的值如下:
0012FE00                                   43 48 53 00                CHS.

00D1475A     33F6         xor esi,esi
00D1475C     80BD E3FEFFF>cmp byte ptr ss:[ebp-11D],0     ; 堆栈 ss:[0012FCFB]=00  这个地址就是文件的路径地址
00D14763     0F84 AF00000>je 00D14818

00D14818     8BC6         mov eax,esi                     ; 得到0
00D1481A     5E           pop esi
00D1481B     5B           pop ebx                         ; esi=003C0000,ebx=00D2E5B4
00D1481C     8BE5         mov esp,ebp                     ; 释放局部变量,恢复指针
00D1481E     5D           pop ebp
00D1481F     C3           retn                                        ;返回到00D157AF

00D157AA     E8 B5EEFFFF  call 00D14664                   ; 里面调用了好几个函数,在我的电脑上最终得到的就是当前程序的完整文件路径
00D157AF     8BD8         mov ebx,eax
00D157B1     891D B4B0D30>mov dword ptr ds:[D3B0B4],ebx
00D157B7     85DB         test ebx,ebx
00D157B9     75 0A        jnz short 00D157C5
00D157BB     A1 A8B0D300  mov eax,dword ptr ds:[<00D157F5>; ds:[00D3B0A8]=00D10000
00D157C0     A3 B4B0D300  mov dword ptr ds:[D3B0B4],eax
00D157C5     B8 A4B0D300  mov eax,0D3B0A4
00D157CA     E8 EDF0FFFF  call 00D148BC

00D148BC     8B15 10B0D30>mov edx,dword ptr ds:[D3B010]
00D148C2     8910         mov dword ptr ds:[eax],edx
00D148C4     A3 10B0D300  mov dword ptr ds:[D3B010],eax   ; eax=00D3B0A4存到了【00D3B010】
00D148C9     C3           retn

保存了几个值,继续

00D157CF     81C4 0801000>add esp,108
00D157D5     5B           pop ebx
00D157D6     C3           retn

恢复堆栈,继续

00D1580B     5A           pop edx
00D1580C     58           pop eax                         ; eax=00D3A6C8,edx=00D3B0A4
00D1580D     FF35 D8C4D30>push dword ptr ds:[D3C4D8]
00D15813     B9 BCB0D300  mov ecx,0D3B0BC                 ; ecx=00D3B0BC
00D15818     E8 1BDCFFFF  call 00D13438

00D13438     51           push ecx
00D13439     56           push esi
00D1343A     57           push edi
00D1343B     BE 9CC4D300  mov esi,0D3C49C
00D13440     8D7D C4      lea edi,dword ptr ss:[ebp-3C]
00D13443     B9 0B000000  mov ecx,0B
00D13448     F3:A5        rep movs dword ptr es:[edi],dwo>;esi里都是00,所以edi被填充了0x0B的00
我们记录一下它覆盖的地址0012FF4C--0012FF74
00D1344A     8F05 BCC4D30>pop dword ptr ds:[D3C4BC]
00D13450     8F05 B8C4D30>pop dword ptr ds:[D3C4B8]
00D13456     892D B0C4D30>mov dword ptr ds:[D3C4B0],ebp
00D1345C     891D B4C4D30>mov dword ptr ds:[D3C4B4],ebx
00D13462     8905 A4C4D30>mov dword ptr ds:[D3C4A4],eax
00D13468     8915 ACC4D30>mov dword ptr ds:[D3C4AC],edx
00D1346E     8D4D C4      lea ecx,dword ptr ss:[ebp-3C]
00D13471     890D 9CC4D30>mov dword ptr ds:[D3C49C],ecx

这些指令在内存里填充出了如下的结构:
00D3C490  00 00 00 00 00 00 00 00 00 00 00 00 4C FF 12 00  ............L_.
00D3C4A0  00 00 00 00 C8 A6 D3 00 00 00 00 00 A4 B0 D3 00  ....圈?....ぐ?
00D3C4B0   88 FF 12  00 B4 E5  D2 00  00 00  3C 00 00  00 D1 00  ?_.村?..<...?

00D13477     31C9         xor ecx,ecx
00D13479     837D 0C 00   cmp dword ptr ss:[ebp+C],0      ; 堆栈 ss:[0012FF94]=00000001
00D1347D     75 02        jnz short 00D13481

00D13481     890D A8>mov dword ptr ds:[D3C4A8],ecx
00D13487     E8 A4FE>call 00D13330

继续

00D13330     31D2    xor edx,edx
00D13332     8D45 F4 lea eax,dword ptr ss:[ebp-C]
00D13335     64:8B0A mov ecx,dword ptr fs:[edx]
00D13338     64:8902 mov dword ptr fs:[edx],eax
00D1333B     8908    mov dword ptr ds:[eax],ecx
00D1333D     C740 04>mov dword ptr ds:[eax+4],0D13294
00D13344     8968 08 mov dword ptr ds:[eax+8],ebp
00D13347     8905 A0>mov dword ptr ds:[D3C4A0],eax      ; 这段代码构建了一个SEH结构,但是它采用了一小点的迂回方法,但是最终我们还是可以在堆栈中看到
00D1334D     C3      retn

间接的去构建SEH结构,我们只要看到FS[0]就要想到SEH,这里你不注意的话,还是有可能错过了的
0012FF7C   0012FFE0  指针到下一个 SEH 记录
0012FF80   00D13294  SE 句柄

00D1348C     8B45 0C mov eax,dword ptr ss:[ebp+C]       ; 上面的哪一个比较数据
00D1348F     40      inc eax
00D13490     A2 C0C4>mov byte ptr ds:[D3C4C0],al        ; 把结果存到【00D3C4C0],数据窗口注意跟踪这段内存
00D13495     48      dec eax
00D13496     59      pop ecx
00D13497     8B11    mov edx,dword ptr ds:[ecx]
00D13499     8915 C4>mov dword ptr ds:[D3C4C4],edx      ; 继续的在往上面的连续内存地址存入,
00D1349F     74 03   je short 00D134A4
00D134A1     FF1481  call dword ptr ds:[ecx+eax*4]

再往内存里存入东西,就是前面我们列出过的内存
00D3C490  00 00 00 00 00 00 00 00 00 00 00 00 4C FF 12 00  ............L_.
00D3C4A0  7C FF 12 00 C8 A6 D3 00 00 00 00 00 A4 B0 D3 00  |_.圈?....ぐ?
00D3C4B0   88 FF 12  00 B4 E5  D2 00  00 00  3C 00 00 00 D1  00  ?_.村?..<...?
00D3C4C0   02 00 00  00 24 57  D1 00  01 00  00 00  00 00 00  00  _...$W?_.......

00D156D4     B8 0800>mov eax,8
00D156D9     85C0    test eax,eax
00D156DB     74 1D   je short 00D156FA
00D156DD     E8 82FF>call 00D15664                      ; jmp to kernel32.TlsAlloc
00D156E2     A3 CCC4>mov dword ptr ds:[<Tls index>],eax ; 存到了00D3C4CC,eax=00000005
00D156E7     85C0    test eax,eax
00D156E9     7D 0A   jge short 00D156F5                 ; 函数是否成功

调用TlsAlloc函数
The TlsAlloc function allocates a thread local storage (TLS) index. Any thread of the process can subsequently use this index to store and retrieve values that are local to the thread.
继续

00D156F5     E8 8AFF>call 00D15684
00D156FA     C3      retn

我们看看,获得TLS index后,它要做什么

00D15684     53      push ebx
00D15685     B8 0800>mov eax,8
00D1568A     85C0    test eax,eax
00D1568C     74 43   je short 00D156D1
00D1568E     833D CC>cmp dword ptr ds:[<Tls index>],0   ; 比较是不是零,我们已经说了,我这里得到的是05
00D15695     7D 0A   jge short 00D156A1

00D156A1     68 0800>push 8
00D156A6     6A 40   push 40
00D156A8     E8 A7FF>call 00D15654                      ; jmp to kernel32.LocalAlloc
00D156AD     8BD8    mov ebx,eax
00D156AF     85DB    test ebx,ebx                                ;检测是否成功
00D156B1     75 0C   jnz short 00D156BF

The LocalAlloc function allocates the specified number of bytes from the heap,分配了8字节,返回值等于00144800

00D156BF     53      push ebx
00D156C0     A1 CCC4>mov eax,dword ptr ds:[<Tls index>]
00D156C5     50      push eax
00D156C6     E8 B1FF>call 00D1567C                      ; jmp to kernel32.TlsSetValue
00D156CB     891D E0>mov dword ptr ds:[<heap addr>],ebx
00D156D1     5B      pop ebx                            ; 00D2E5B4
00D156D2     C3      retn

关于这个函数和TLS的最简单的资料:
The TlsSetValue function stores a value in the calling thread's thread local storage (TLS) slot for a specified TLS index. Each thread of a process has its own slot for each TLS index.
TLS indexes are typically allocated by the TlsAlloc function during process or DLL initialization. Once allocated, each thread of the process can use a TLS index to access its own TLS storage slot for that index. The storage slot for each thread is initialized to NULL. A thread specifies a TLS index in a call to TlsSetValue, to store a value in its slot. The thread specifies the same index in a subsequent call to TlsGetValue, to retrieve the stored value. 调用时的,堆栈参数值:
0012FF1C   00000005  |TlsIndex = 5
0012FF20   00144800  \pValue = 00144800
保存heap地址后返回

00D156F5     E8 8AFF>call 00D15684
00D156FA     C3      retn                                        ;返回到这里,再次返回

00D134A1     FF1481  call dword ptr ds:[ecx+eax*4]      ; 作用分配TLS,分配heap addr,把堆地址存到TLS
00D134A4     8B5424 >mov edx,dword ptr ss:[esp+4]       ; 来到这里,堆栈 ss:[0012FF34]=00000000
00D134A8     85D2    test edx,edx
00D134AA     74 05   je short 00D134B1

00D134B1     833D 1C>cmp dword ptr ds:[D3C01C],0        ; 地址附近的整片内存都是00
00D134B8     75 0D   jnz short 00D134C7
00D134BA     C605 24>mov byte ptr ds:[D3C024],1         ; 00D3C024
00D134C1     D93D 00>fstcw word ptr ds:[<FPU cw>]       ; 存储FPU控制字
00D134C7     8B45 0C mov eax,dword ptr ss:[ebp+C]       ; 堆栈 ss:[0012FF94]=00000001
00D134CA     48      dec eax
00D134CB     0F85 CB>jnz 00D1359C
00D134D1     E8 02FF>call 00D133D8

继续

00D133D8     55      push ebp
00D133D9     8BEC    mov ebp,esp
00D133DB     53      push ebx
00D133DC     56      push esi
00D133DD     57      push edi
00D133DE     A1 A4C4>mov eax,dword ptr ds:[D3C4A4]        ;这个就是壳放了好多各方数据在其中的地址,数据窗口要跟着
00D133E3     85C0    test eax,eax
00D133E5     74 4B   je short 00D13432
00D133E7     8B30    mov esi,dword ptr ds:[eax]         ; 00000043
00D133E9     33DB    xor ebx,ebx
00D133EB     8B78 04 mov edi,dword ptr ds:[eax+4]       ; ds:[00D3A6CC]=00D3A6D0
00D133EE     33D2    xor edx,edx
00D133F0     55      push ebp
00D133F1     68 1E34>push 0D1341E
00D133F6     64:FF32 push dword ptr fs:[edx]
00D133F9     64:8922 mov dword ptr fs:[edx],esp         ; 又构建了壳的第二个SEH

取出大量的内存值,给寄存器赋值,同时建立了壳的第二个SEH
0012FF10   0012FF7C  指针到下一个 SEH 记录
0012FF14   00D1341E  SE 句柄

00D13400     8B04DF  mov eax,dword ptr ds:[edi+ebx*8]   ; ds:[00D3A6D0]=00D15850,ebx成了寻址因子=0
00D13403     43      inc ebx                            ; 给它加一
00D13404     891D A8>mov dword ptr ds:[<index 00D13400>>; 存储到00D3C490这一片,已经汇聚了很多各种数据的内存地址
00D1340A     85C0    test eax,eax
00D1340C    /74 02   je short 00D13410
00D1340E    |FFD0    call eax                                ;eax=00D15850

00D15850     832D DC>sub dword ptr ds:[D3C4DC],1        ; 得到0xFFFFFFFF
00D15857     C3      retn

我们再来粘贴一次00D3C490这一片内存:
00D3C490  00 00 00 00 00 00 00 00 00 00 00 00 4C FF 12 00  ............L_.
00D3C4A0  7C FF 12 00 C8 A6 D3 00 01 00 00 00 A4 B0 D3 00  |_.圈?_...ぐ?
00D3C4B0  88  FF 12  00 B4  E5 D2 00  00 00  3C 00 00 00  D1 00  ?_.村?..<...?
00D3C4C0  02  00 00  00 24  57 D1 00  01 00  00  00 05 00  00 00  _...$W?_..._...
00D3C4D0  00  00 00  00 00  00 D1 00  00 00  00  00 FF FF  FF FF  ......?....
00D3C4E0 >00 48 14 00 00 00 00 00 00 00 00 00 00 00 00 00  .H_.............
这里面包含了太多的数据,而且来自各个方向,每一个数据几乎都有一段“历史”才存进来的。

00D13410     3BF3    cmp esi,ebx                        ; 果然在这里增加了寻址因子,跳回去读取其他地方的数据
00D13412   ^ 7F EC   jg short 00D13400

我们要来仔细观察一下这个循环
00D13400     8B04DF  mov eax,dword ptr ds:[edi+ebx*8]   ; ds:[00D3A6D0]=00D15850,ebx成了寻址因子
00D13403     43      inc ebx                            ; 给它加一
00D13404     891D A8>mov dword ptr ds:[<index 00D13400>>; 存储到00D3C490这一片,已经汇聚了很多各种数据的内存地址
00D1340A     85C0    test eax,eax
00D1340C     74 02   je short 00D13410
00D1340E     FFD0    call eax
00D13410     3BF3    cmp esi,ebx                        ; 果然在这里增加了寻址因子,跳回去读取其他地方的数据
00D13412   ^ 7F EC   jg short 00D13400
这个循环古怪的地方在于call eax这一句,它通过修改ebx让eax每次读取到不同的数据,而执行这一句时,eax就会跳转到了不同的地方执行,而且,它的跳出循环的条件是esi=ebx,esi里面已经放入了43,ebx是每次加一,所以,这段循环要执行43次,这意味着我们要进入43个call,我的老天,杀了我吧,没办法呀只有继续。
2008-4-19 09:38
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
跟踪ASProtect比跟踪TeLock有意思多了,ASProtect有很多精秒代码,而且还可以复习好多东西
动态获取kernel32,dll库,得到里面的函数
动态申请内存后把自身搬过去执行,然后你就可以看到它手动进行代码的重定位,
其实壳要使用n多的函数,所以你还可以看到壳利用GetProAddresshe和LoadLibrary来得到一大堆的它想要的函数,
申请TLS
寻找E8和E9然后修改后面的数据,E8和E9就是call和jmp指令,这样就可以跳到其他地方了
把多个call的地址放在内存里,然后在循环里放到寄存器里,然后在进行跳转,
有意思的地方太多了,要继续
2008-4-19 09:49
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
学习了~谢谢!
2008-4-20 08:30
0
游客
登录 | 注册 方可回帖
返回
//