【文章标题】: Aspack 2.2 简易分析
【文章作者】: wuhanqi
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
这次分析用的ASPACK是Regkiller工具包里面的那个、
随便找了一个Delphi的加壳...
0041B001 > 60 PUSHAD ; EP
0041B002 E8 03000000 CALL 0041B00A
0041B007 90 NOP ; 花指令 nop
0041B008 EB 04 JMP SHORT 0041B00E
0041B00A 5D POP EBP
0041B00B 45 INC EBP
0041B00C 55 PUSH EBP
0041B00D C3 RETN
0041B00E E8 01000000 CALL 0041B014
0041B013 90 NOP ; 花指令 nop
0041B014 5D POP EBP ; 0041b013出栈到EBP
0041B015 BB EDFFFFFF MOV EBX,-13 ; EBX=-13
0041B01A 03DD ADD EBX,EBP ; EBX=EBP+EBX=0041B000
0041B01C 81EB 00B00100 SUB EBX,1B000 ; EBX=EBX-1B000=00400000 获取基址
0041B022 83BD 7D040000 0>CMP DWORD PTR SS:[EBP+47D],0 ; 比较EBP+47D处数据是否为0
0041B029 899D 7D040000 MOV DWORD PTR SS:[EBP+47D],EBX ; 把基址放到EBP+47D处
0041B02F 0F85 C0030000 JNZ 0041B3F5 ; EBP+47D不为零的话就去OEP!
0041B035 8D85 89040000 LEA EAX,DWORD PTR SS:[EBP+489] ; 获取kernel32.dll 放到eax
0041B03B 50 PUSH EAX ; kernel32.dll压栈
0041B03C FF95 090F0000 CALL DWORD PTR SS:[EBP+F09] ; 调用GetModuleHandleA获取kernel32的基址
0041B042 8985 81040000 MOV DWORD PTR SS:[EBP+481],EAX ; kernel32的基址到EBP+481
0041B048 8BF0 MOV ESI,EAX ; ESI=EAX
0041B04A 8D7D 51 LEA EDI,DWORD PTR SS:[EBP+51] ; 获取VirtualAlloc 放到EDI
0041B04D 57 PUSH EDI ; EDI压栈
0041B04E 56 PUSH ESI ; kernel32的基址压栈
0041B04F FF95 050F0000 CALL DWORD PTR SS:[EBP+F05] ; 调用GetProcAddress获取EDI的地址
0041B055 AB STOSD ; 把获取到的地址放到原来字符串所在的位置
0041B056 B0 00 MOV AL,0
0041B058 AE SCASB
0041B059 ^ 75 FD JNZ SHORT 0041B058 ; 上面几句比较难读,作用是获取下一个函数名称
0041B05B 3807 CMP BYTE PTR DS:[EDI],AL
0041B05D ^ 75 EE JNZ SHORT 0041B04D ; 一共获取了这三个函数:VirtualAlloc VirtualFree VirtualProtect
0041B05F 8D45 7A LEA EAX,DWORD PTR SS:[EBP+7A] ; EAX=0041b013+7a
0041B062 FFE0 JMP EAX ; 去EAX所指向的地址
继续:
0041B08D . 8B9D 8D050000 MOV EBX,DWORD PTR SS:[EBP+58D] ; 41b013+58d的DWORD到EBX
0041B093 . 0BDB OR EBX,EBX ; 看看是不是零
0041B095 . 74 0A JE SHORT 0041B0A1 ; 是就跳
0041B097 . 8B03 MOV EAX,DWORD PTR DS:[EBX]
0041B099 . 8785 91050000 XCHG DWORD PTR SS:[EBP+591],EAX
0041B09F . 8903 MOV DWORD PTR DS:[EBX],EAX
0041B0A1 > 8DB5 BD050000 LEA ESI,DWORD PTR SS:[EBP+5BD] ; 获取保存第一个区段的偏移1000的地址 放到ESI
0041B0A7 . 833E 00 CMP DWORD PTR DS:[ESI],0 ; 看看偏移的值是不是0
0041B0AA . 0F84 15010000 JE 0041B1C5 ; 是就跳
0041B0B0 . 6A 04 PUSH 4
0041B0B2 . 68 00100000 PUSH 1000
0041B0B7 . 68 00180000 PUSH 1800
0041B0BC . 6A 00 PUSH 0
0041B0BE . FF55 51 CALL DWORD PTR SS:[EBP+51] ; 调用了VirtualAlloc申请了一段1800大小的空间
0041B0C1 . 8985 53010000 MOV DWORD PTR SS:[EBP+153],EAX ; 把获取到的地址放到EBP+153
0041B0C7 > 8B46 04 MOV EAX,DWORD PTR DS:[ESI+4] ; 源程序获取第一个区段的大小到EAX
0041B0CA . 05 0E010000 ADD EAX,10E ; EAX=EAX+10E
0041B0CF . 6A 04 PUSH 4
0041B0D1 . 68 00100000 PUSH 1000
0041B0D6 . 50 PUSH EAX
0041B0D7 . 6A 00 PUSH 0
0041B0D9 . FF55 51 CALL DWORD PTR SS:[EBP+51] ; 调用了VirtualAlloc申请了一段EAX大小的空间
0041B0DC . 8985 4F010000 MOV DWORD PTR SS:[EBP+14F],EAX ; 申请的地址到EBP+14F
0041B0E2 . 56 PUSH ESI ; ESI压栈
0041B0E3 . 8B1E MOV EBX,DWORD PTR DS:[ESI] ; 第一个区段的偏移到ebx
0041B0E5 . 039D 7D040000 ADD EBX,DWORD PTR SS:[EBP+47D] ; 偏移与基址相加00401000
0041B0EB . FFB5 53010000 PUSH DWORD PTR SS:[EBP+153] ; 第一次申请的空间地址入栈
0041B0F1 . FF76 04 PUSH DWORD PTR DS:[ESI+4] ; 第一个区段大小入栈
0041B0F4 . 50 PUSH EAX ; 第二个空间地址入栈
0041B0F5 . 53 PUSH EBX ; 00401000入栈
0041B0F6 . E8 2D050000 CALL 0041B628 ; 开始解码
0041B0FB . B3 01 MOV BL,1 ; bl=0
0041B0FD . 80FB 00 CMP BL,0 ; 比较是否为0
0041B100 . 75 5E JNZ SHORT 0041B160 ; 不为零就跳
0041B102 . FE85 E9000000 INC BYTE PTR SS:[EBP+E9] ; 更改上面的mov bl,0为mov bl,1 不太懂这几个指令干嘛的..
0041B108 . 8B3E MOV EDI,DWORD PTR DS:[ESI] ; EDI等于第一个区段的偏移
0041B10A . 03BD 7D040000 ADD EDI,DWORD PTR SS:[EBP+47D] ; EDI=EDI+基址=00401000
0041B110 . FF37 PUSH DWORD PTR DS:[EDI] ; 把00401000处的Dword压栈
0041B112 . C607 C3 MOV BYTE PTR DS:[EDI],0C3 ; 修改第一个字节为C3
0041B115 . FFD7 CALL EDI ; 然后call 00401000 被刚刚修改的c3给退出来了...
0041B117 . 8F07 POP DWORD PTR DS:[EDI] ; 再恢复数据 晕 作孽啊
0041B119 . 50 PUSH EAX ; 第一个区段大小入栈
0041B11A . 51 PUSH ECX ; ECX入栈 此时为1
0041B11B . 56 PUSH ESI ; 把存放偏移的地址压栈
0041B11C . 53 PUSH EBX ; 00401000入栈
0041B11D . 8BC8 MOV ECX,EAX ; ECX=EAX
0041B11F . 83E9 06 SUB ECX,6 ; ECX=ECX-6
0041B122 . 8BB5 4F010000 MOV ESI,DWORD PTR SS:[EBP+14F] ; 第二次申请的地址到ESI
0041B128 . 33DB XOR EBX,EBX ; 清零EBX
0041B12A > 0BC9 OR ECX,ECX ; 看看ecx是不是0
0041B12C . 74 2E JE SHORT 0041B15C ; 是就跑
0041B12E . 78 2C JS SHORT 0041B15C
0041B130 . AC LODSB ; 逐个获取第二次申请的空间里面的字节 放到al
0041B131 . 3C E8 CMP AL,0E8 ; 看看是不是E8
0041B133 . 74 0A JE SHORT 0041B13F ; 是就跳转
0041B135 . EB 00 JMP SHORT 0041B137
0041B137 > 3C E9 CMP AL,0E9 ; 看看是不是E9
0041B139 . 74 04 JE SHORT 0041B13F ; 是就跳转
0041B13B > 43 INC EBX ; 否则EBX+1
0041B13C . 49 DEC ECX ; ECX-1
0041B13D .^ EB EB JMP SHORT 0041B12A
0041B13F > 8B06 MOV EAX,DWORD PTR DS:[ESI] ; 把E8/E9后面的DWORD放到EAX
0041B141 . EB 00 JMP SHORT 0041B143
0041B143 > 803E 00 CMP BYTE PTR DS:[ESI],0 ; 看看E8/E9后第一个字节是不是0
0041B146 .^ 75 F3 JNZ SHORT 0041B13B ; 不是就跳了
0041B148 . 24 00 AND AL,0 ; AL=AL and 0
0041B14A . C1C0 18 ROL EAX,18 ; eax=eax rol 18
0041B14D . 2BC3 SUB EAX,EBX ; eax=eax-ebx
0041B14F . 8906 MOV DWORD PTR DS:[ESI],EAX ; 把运算后的EAX放回去
0041B151 . 83C3 05 ADD EBX,5 ; EBX=EBX+5
0041B154 . 83C6 04 ADD ESI,4 ; ESI=ESI+4
0041B157 . 83E9 05 SUB ECX,5 ; ECX=ECX-4
0041B15A .^ EB CE JMP SHORT 0041B12A ; 跳回去,敢情上面在修复跳转和call
0041B15C > \5B POP EBX ; 把之前压进去的东西都弹出来
0041B15D . 5E POP ESI
0041B15E . 59 POP ECX
0041B15F . 58 POP EAX
0041B160 > EB 08 JMP SHORT 0041B16A
继续:
0041B16A > \8BC8 MOV ECX,EAX ; 第一个区段大小到ECX
0041B16C . 8B3E MOV EDI,DWORD PTR DS:[ESI] ; 偏移到EDI
0041B16E . 03BD 7D040000 ADD EDI,DWORD PTR SS:[EBP+47D] ; 偏移加上基址=00401000
0041B174 . 8BB5 4F010000 MOV ESI,DWORD PTR SS:[EBP+14F] ; 第二次申请的空间地址到ESI
0041B17A . C1F9 02 SAR ECX,2 ; ECX算术右移两位
0041B17D . F3:A5 REP MOVSD ; 转移数据咯
0041B17F . 8BC8 MOV ECX,EAX ; ECX=EAX
0041B181 . 83E1 03 AND ECX,3 ; ECX=ECX AND 3
0041B184 . F3:A4 REP MOVSB ; 再转移数据..
0041B186 . 5E POP ESI ; 存放偏移的地址出栈
0041B187 . 68 00800000 PUSH 8000
0041B18C . 6A 00 PUSH 0
0041B18E . FFB5 4F010000 PUSH DWORD PTR SS:[EBP+14F]
0041B194 . FF55 5E CALL DWORD PTR SS:[EBP+5E] ; 释放刚刚申请的空间
0041B197 . 83C6 0C ADD ESI,0C ; ESI=ESI+0C
0041B19A . 833E 00 CMP DWORD PTR DS:[ESI],0 ; 看看ESI处dword东西是不是0
0041B19D .^ 0F85 24FFFFFF JNZ 0041B0C7 ; 这边回去循环了 就第一个区段变第二个区段..
0041B1A3 . 68 00800000 PUSH 8000
0041B1A8 . 6A 00 PUSH 0
0041B1AA . FFB5 53010000 PUSH DWORD PTR SS:[EBP+153]
0041B1B0 . FF55 5E CALL DWORD PTR SS:[EBP+5E] ; 把第一次申请的空间释放掉
…………………… 个人感觉中间这部分代码无意义 程序都跳过去了 我就不分析了 在此略过 ……………………
0041B26F BE 00A00000 MOV ESI,0A000 ; ESI=A000
0041B274 8B95 7D040000 MOV EDX,DWORD PTR SS:[EBP+47D] ; 基址到EDX
0041B27A 03F2 ADD ESI,EDX ; ESI=ESI+EDX=0040A000
0041B27C 8B46 0C MOV EAX,DWORD PTR DS:[ESI+C] ; esi+c 的数据到EAX
0041B27F 85C0 TEST EAX,EAX ; 看看是不是0
0041B281 0F84 0D010000 JE 0041B394 ; 是就跳
0041B287 03C2 ADD EAX,EDX ; EAX=EAX+基址 获取了DLL名称
0041B289 8BD8 MOV EBX,EAX ; EBX=EAX
0041B28B 50 PUSH EAX ; EAX压栈
0041B28C FF95 090F0000 CALL DWORD PTR SS:[EBP+F09] ; 调用GetModuleHandleA 获取DLL基址
0041B292 85C0 TEST EAX,EAX ; 看看获取到了没
0041B294 75 07 JNZ SHORT 0041B29D ; 获取到了就跳
0041B296 53 PUSH EBX ; 否则会载入这个DLL
0041B297 FF95 0D0F0000 CALL DWORD PTR SS:[EBP+F0D]
0041B29D 8985 A1050000 MOV DWORD PTR SS:[EBP+5A1],EAX ; KERNEL32的基址放到EBP+5A1
0041B2A3 C785 A5050000 0>MOV DWORD PTR SS:[EBP+5A5],0 ; EBP+5A5=0 清零了存放KERNEL32基址后的那个DWORD
0041B2AD 8B95 7D040000 MOV EDX,DWORD PTR SS:[EBP+47D] ; 程序基址到EDX
0041B2B3 8B06 MOV EAX,DWORD PTR DS:[ESI] ; ESI的数据到EAX
0041B2B5 85C0 TEST EAX,EAX ; 看看是不是0
0041B2B7 75 03 JNZ SHORT 0041B2BC ; 是就跳
0041B2B9 8B46 10 MOV EAX,DWORD PTR DS:[ESI+10] ; ESI+10的数据放到EAX
0041B2BC 03C2 ADD EAX,EDX ; EAX=EAX+基址=0040a1d0
0041B2BE 0385 A5050000 ADD EAX,DWORD PTR SS:[EBP+5A5] ; EAX = EAX 加上EBP+5A5的数据
0041B2C4 8B18 MOV EBX,DWORD PTR DS:[EAX] ; 把EAX的数据放到EBX
0041B2C6 8B7E 10 MOV EDI,DWORD PTR DS:[ESI+10] ; ESI+10的数据放到EAX
0041B2C9 03FA ADD EDI,EDX ; EDI加上基址
0041B2CB 03BD A5050000 ADD EDI,DWORD PTR SS:[EBP+5A5] ; EDI与EBP+5A5的数据做加法运算 此时EDI=EAX
0041B2D1 85DB TEST EBX,EBX ; 看看EBX是不是零 此时EBX是从eax获取到的数据
0041B2D3 /0F84 A5000000 JE 0041B37E ; 是就跳转 结束填充IAT
0041B2D9 F7C3 00000080 TEST EBX,80000000 ; 看看等不等于80000000
0041B2DF 75 04 JNZ SHORT 0041B2E5 ; 相等就跳
0041B2E1 03DA ADD EBX,EDX ; EBX=EBX+基址
0041B2E3 43 INC EBX ; EBX+1
0041B2E4 43 INC EBX ; EBX+1
0041B2E5 53 PUSH EBX ; EBX压栈 此时获取到了函数名称
0041B2E6 81E3 FFFFFF7F AND EBX,7FFFFFFF ; EBX = EBX AND 7FFFFFFF
0041B2EC 53 PUSH EBX ; EBX再压栈
0041B2ED FFB5 A1050000 PUSH DWORD PTR SS:[EBP+5A1] ; 把kernel32的基址压栈
0041B2F3 FF95 050F0000 CALL DWORD PTR SS:[EBP+F05] ; GetProcAddress 获取函数地址
0041B2F9 85C0 TEST EAX,EAX ; 看看获取到没
0041B2FB 5B POP EBX ; 之前压了两次函数名 出栈一次 数据放到EBX
0041B2FC 75 72 JNZ SHORT 0041B370 ; 获取到了就跳转
跳转来到:
0041B370 > \8907 MOV DWORD PTR DS:[EDI],EAX ; 填充数据到EDI 即是填充IAT 修复输入表
0041B372 . 8385 A5050000>ADD DWORD PTR SS:[EBP+5A5],4 ; [EBP+5A5]+4
0041B379 .^ E9 2FFFFFFF JMP 0041B2AD ; 跳回去
…………………………中间还调用了几次 VirtualProtect 不知道干吗的 没看懂 不过感觉壳该干完的好像差不多了 遂没分析 ……………………
0041B3F5 B8 5C670000 MOV EAX,675C ; EAX=OEP的VA
0041B3FA 50 PUSH EAX ; 压栈
0041B3FB 0385 7D040000 ADD EAX,DWORD PTR SS:[EBP+47D] ; EAX=EAX加基址
0041B401 59 POP ECX ; ECX出栈 此时ecx为OEP的VA
0041B402 0BC9 OR ECX,ECX ; 看看是不是0
0041B404 8985 03040000 MOV DWORD PTR SS:[EBP+403],EAX ; 把OEP放到EBP+403 即修改下面的push 方便我们去OEP
0041B40A 61 POPAD
0041B40B 75 08 JNZ SHORT 0041B415 ; 不是0就跳
0041B40D B8 01000000 MOV EAX,1
0041B412 C2 0C00 RETN 0C
0041B415 68 5C674000 PUSH 0040675C ; OEP压栈
0041B41A C3 RETN ; ...然后就该干嘛干嘛
--------------------------------------------------------------------------------
【经验总结】
第一次分析压缩壳...不知道哪里会有错,希望大牛们能指出
能有基础分析这个要感谢天草的矛与盾的趣味,讲的非常非常细.. 还有网上做过分析的前辈们,或多或少的我可能都看过
文章,呵呵~
夜深人静脑子不好使,错了大家千万别骂我,希望你能指出,觉得好的,不吝啬的,就加点UB.UF啦,嘿嘿~
--------------------------------------------------------------------------------
【版权声明】: 依旧没版权..学习学习再学习!
2010年02月03日 23:46:38
本代码的着色效果由xTiNt自动完成
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)