-
-
[原创]单步跟踪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
加密的对象是罗云彬书的第四章的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
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
他的文章
看原图
赞赏
雪币:
留言: