通过上面的分析可以看出,UPX用了部分的代码混淆,不过最后的执行流程是:
以上就是upx壳主要的执行流程,脱还是很好脱的
0046DF7D 90 NOP
0046DF7E 90 NOP
0046DF7F 90 NOP ; 花指令
0046DF80 > 60 PUSHAD
0046DF81 BE 00704400 MOV ESI,00_upx.00447000
0046DF86 8DBE 00A0FBFF LEA EDI,DWORD PTR DS:[ESI+0xFFFBA000] ; edi = 第一个区段的va
0046DF8C C787 9C400500 4>MOV DWORD PTR DS:[EDI+0x5409C],0xD05CFA4>
0046DF96 57 PUSH EDI ; 第一个区段的va入栈
0046DF97 83CD FF OR EBP,0xFFFFFFFF ; ebp=-1
0046DF9A EB 0E JMP SHORT 00_upx.0046DFAA
0046DF9C 90 NOP
0046DF9D 90 NOP
0046DF9E 90 NOP
0046DF9F 90 NOP
0046DFA0 8A06 MOV AL,BYTE PTR DS:[ESI]
0046DFA2 46 INC ESI
0046DFA3 8807 MOV BYTE PTR DS:[EDI],AL
0046DFA5 47 INC EDI
0046DFA6 01DB ADD EBX,EBX
0046DFA8 75 07 JNZ SHORT 00_upx.0046DFB1
0046DFAA 8B1E MOV EBX,DWORD PTR DS:[ESI] ; ebx = FFFE77BF
0046DFAC 83EE FC SUB ESI,-0x4 ; esi -= 0x4
0046DFAF 11DB ADC EBX,EBX
0046DFB1 ^ 72 ED JB SHORT 00_upx.0046DFA0 ; 把区段头表的4个字段填充了
0046DFB3 B8 01000000 MOV EAX,0x1 ; eax =0x1
0046DFB8 01DB ADD EBX,EBX
0046DFBA 75 07 JNZ SHORT 00_upx.0046DFC3
0046DFBC 8B1E MOV EBX,DWORD PTR DS:[ESI]
0046DFBE 83EE FC SUB ESI,-0x4
0046DFC1 11DB ADC EBX,EBX
0046DFC3 11C0 ADC EAX,EAX ; eax += eax,带进位
0046DFC5 01DB ADD EBX,EBX ; 这个ebx+ebx可能就是想有个进位
0046DFC7 73 0B JNB SHORT 00_upx.0046DFD4
0046DFC9 75 28 JNZ SHORT 00_upx.0046DFF3
0046DFCB 8B1E MOV EBX,DWORD PTR DS:[ESI]
0046DFCD 83EE FC SUB ESI,-0x4
0046DFD0 11DB ADC EBX,EBX
0046DFD2 72 1F JB SHORT 00_upx.0046DFF3
0046DFD4 48 DEC EAX
0046DFD5 01DB ADD EBX,EBX
0046DFD7 75 07 JNZ SHORT 00_upx.0046DFE0
0046DFD9 8B1E MOV EBX,DWORD PTR DS:[ESI]
0046DFDB 83EE FC SUB ESI,-0x4
0046DFDE 11DB ADC EBX,EBX
0046DFE0 11C0 ADC EAX,EAX
0046DFE2 ^ EB D4 JMP SHORT 00_upx.0046DFB8
0046DFE4 01DB ADD EBX,EBX
0046DFE6 75 07 JNZ SHORT 00_upx.0046DFEF
0046DFE8 8B1E MOV EBX,DWORD PTR DS:[ESI]
0046DFEA 83EE FC SUB ESI,-0x4
0046DFED 11DB ADC EBX,EBX
0046DFEF 11C9 ADC ECX,ECX ; 通过ebx 的自家,然后使ecx 通过进位增加
0046DFF1 EB 52 JMP SHORT 00_upx.0046E045
0046DFF3 31C9 XOR ECX,ECX ; ecx = 0
0046DFF5 83E8 03 SUB EAX,0x3 ; eax -= 0x3
0046DFF8 72 11 JB SHORT 00_upx.0046E00B
0046DFFA C1E0 08 SHL EAX,0x8 ; eax 左移8位,这是干嘛啊
0046DFFD 8A06 MOV AL,BYTE PTR DS:[ESI]
0046DFFF 46 INC ESI
0046E000 83F0 FF XOR EAX,0xFFFFFFFF ; 上面这个3条还是没有看懂
0046E003 74 75 JE SHORT 00_upx.0046E07A
0046E005 D1F8 SAR EAX,1 ; eax /2
0046E007 89C5 MOV EBP,EAX ; ebp =eax
0046E009 EB 0B JMP SHORT 00_upx.0046E016
0046E00B 01DB ADD EBX,EBX
0046E00D 75 07 JNZ SHORT 00_upx.0046E016
0046E00F 8B1E MOV EBX,DWORD PTR DS:[ESI]
0046E011 83EE FC SUB ESI,-0x4
0046E014 11DB ADC EBX,EBX
0046E016 ^ 72 CC JB SHORT 00_upx.0046DFE4
0046E018 41 INC ECX ; ecx +=1
0046E019 01DB ADD EBX,EBX ; 天哪,这是要干嘛啊
0046E01B 75 07 JNZ SHORT 00_upx.0046E024
0046E01D 8B1E MOV EBX,DWORD PTR DS:[ESI]
0046E01F 83EE FC SUB ESI,-0x4
0046E022 11DB ADC EBX,EBX
0046E024 ^ 72 BE JB SHORT 00_upx.0046DFE4
0046E026 01DB ADD EBX,EBX
0046E028 75 07 JNZ SHORT 00_upx.0046E031
0046E02A 8B1E MOV EBX,DWORD PTR DS:[ESI]
0046E02C 83EE FC SUB ESI,-0x4
0046E02F 11DB ADC EBX,EBX
0046E031 11C9 ADC ECX,ECX
0046E033 01DB ADD EBX,EBX
0046E035 ^ 73 EF JNB SHORT 00_upx.0046E026
0046E037 75 09 JNZ SHORT 00_upx.0046E042
0046E039 8B1E MOV EBX,DWORD PTR DS:[ESI]
0046E03B 83EE FC SUB ESI,-0x4
0046E03E 11DB ADC EBX,EBX
0046E040 ^ 73 E4 JNB SHORT 00_upx.0046E026
0046E042 83C1 02 ADD ECX,0x2
0046E045 81FD 00FBFFFF CMP EBP,-0x500 ; 没明白,ebp跟0x-500比什么啊,难道是循环0x500次?
0046E04B 83D1 02 ADC ECX,0x2 ; ecx +=0x2 ,带进位
0046E04E 8D142F LEA EDX,DWORD PTR DS:[EDI+EBP] ; edx = 区段表的的某个地址,应该是VirtualAddress-1个字段
0046E051 83FD FC CMP EBP,-0x4
0046E054 76 0E JBE SHORT 00_upx.0046E064
0046E056 8A02 MOV AL,BYTE PTR DS:[EDX]
0046E058 42 INC EDX ; edx = VirtualAddress
0046E059 8807 MOV BYTE PTR DS:[EDI],AL
0046E05B 47 INC EDI ; edi +=0x1
0046E05C 49 DEC ECX ; ecx -=1,没明白什么意思啊
0046E05D ^ 75 F7 JNZ SHORT 00_upx.0046E056
0046E05F ^ E9 42FFFFFF JMP 00_upx.0046DFA6 ; 这个循环应该是把区段头填充了一些内容,但是具体还是没搞清楚
0046E064 8B02 MOV EAX,DWORD PTR DS:[EDX]
0046E066 83C2 04 ADD EDX,0x4
0046E069 8907 MOV DWORD PTR DS:[EDI],EAX
0046E06B 83C7 04 ADD EDI,0x4
0046E06E 83E9 04 SUB ECX,0x4
0046E071 ^ 77 F1 JA SHORT 00_upx.0046E064
0046E073 01CF ADD EDI,ECX
0046E075 ^ E9 2CFFFFFF JMP 00_upx.0046DFA6 ; 这个循环即使从另一个区段upx1中把数据全部放到upx0中
0046E07A 5E POP ESI ; esi 就是区段表的va
0046E07B 89F7 MOV EDI,ESI ; edi=esi = 区段表的va
0046E07D B9 C9290000 MOV ECX,0x29C9 ; ecx = 0x29c9,写死了啊
0046E082 8A07 MOV AL,BYTE PTR DS:[EDI]
0046E084 47 INC EDI ; edi+=1
0046E085 2C E8 SUB AL,0xE8 ; 这里怎么又减了啊,看不明白
0046E087 3C 01 CMP AL,0x1 ; 应该是找jmp和call指令
0046E089 ^ 77 F7 JA SHORT 00_upx.0046E082
0046E08B 803F 14 CMP BYTE PTR DS:[EDI],0x14 ; edi现在是这个call后面的下一个地址,14感觉像是暗号似的
0046E08E ^ 75 F2 JNZ SHORT 00_upx.0046E082
0046E090 8B07 MOV EAX,DWORD PTR DS:[EDI] ; eax = 把目前edi也就是call或jump的地址保存到eax中
0046E092 8A5F 04 MOV BL,BYTE PTR DS:[EDI+0x4] ; bl= call语句的下一条汇编指令的第一个字节
0046E095 66:C1E8 08 SHR AX,0x8 ; 取出低位
0046E099 C1C0 10 ROL EAX,0x10 ; 高低4位进行交换
0046E09C 86C4 XCHG AH,AL ; ah和al的数据进行交换
0046E09E 29F8 SUB EAX,EDI ; ?
0046E0A0 80EB E8 SUB BL,0xE8
0046E0A3 01F0 ADD EAX,ESI
0046E0A5 8907 MOV DWORD PTR DS:[EDI],EAX ; 这里因该是一个算法,把这个e9的地址转换成实际的地址
0046E0A7 83C7 05 ADD EDI,0x5 ; e9 的下一条执行的汇编语句
0046E0AA 88D8 MOV AL,BL
0046E0AC ^ E2 D9 LOOPD SHORT 00_upx.0046E087 ; 要循环ecx次也就是29c9次,因该是有29c9个jmp和call指令
0046E0AE 8DBE 00A00600 LEA EDI,DWORD PTR DS:[ESI+0x6A000] ; edi =0x46b000 ,写死的
0046E0B4 8B07 MOV EAX,DWORD PTR DS:[EDI] ; eax = 0x46b000中保存的值
0046E0B6 09C0 OR EAX,EAX ; 检测eax是不是为0,应该是判断序号是不是为0(或者是切换导另一个导入表的时候)
0046E0B8 74 3C JE SHORT 00_upx.0046E0F6 ; 如果为空就跳转,0x46b000干部不是IAT就是重定位,IAT的可能性比较大
0046E0BA 8B5F 04 MOV EBX,DWORD PTR DS:[EDI+0x4]
0046E0BD 8D8430 6CF30600 LEA EAX,DWORD PTR DS:[EAX+ESI+0x6F36C] ; eax = 导入的dll的名称地址
0046E0C4 01F3 ADD EBX,ESI ; ebx 感觉像是缓冲区
0046E0C6 50 PUSH EAX ; dll 名字入栈
0046E0C7 83C7 08 ADD EDI,0x8
0046E0CA FF96 0CF40600 CALL DWORD PTR DS:[ESI+0x6F40C] ; kernel32.LoadLibraryA
0046E0D0 95 XCHG EAX,EBP ; ebp =load的dll的hModule
0046E0D1 8A07 MOV AL,BYTE PTR DS:[EDI]
0046E0D3 47 INC EDI
0046E0D4 08C0 OR AL,AL ; 如果是00的话说明可能是序号导出?
0046E0D6 ^ 74 DC JE SHORT 00_upx.0046E0B4
0046E0D8 89F9 MOV ECX,EDI ; ecx = 函数的名称
0046E0DA 57 PUSH EDI ; 函数名入栈
0046E0DB 48 DEC EAX
0046E0DC F2:AE REPNE SCAS BYTE PTR ES:[EDI]
0046E0DE 55 PUSH EBP ; hModule入栈
0046E0DF FF96 10F40600 CALL DWORD PTR DS:[ESI+0x6F410] ; kernel32.GetProcAddress
0046E0E5 09C0 OR EAX,EAX ; eax = 函数地址,判断返回值是不是为空
0046E0E7 74 07 JE SHORT 00_upx.0046E0F0
0046E0E9 8903 MOV DWORD PTR DS:[EBX],EAX ; 把函数的地址放到ebx的空间中
0046E0EB 83C3 04 ADD EBX,0x4 ; 0x4586a4 是导入表之一
0046E0EE ^ EB E1 JMP SHORT 00_upx.0046E0D1
0046E0F0 FF96 20F40600 CALL DWORD PTR DS:[ESI+0x6F420]
0046E0F6 8BAE 14F40600 MOV EBP,DWORD PTR DS:[ESI+0x6F414] ; ebp = kernel32.VirtualProtect
0046E0FC 8DBE 00F0FFFF LEA EDI,DWORD PTR DS:[ESI-0x1000] ; edi是程序基地址
0046E102 BB 00100000 MOV EBX,0x1000
0046E107 50 PUSH EAX ; eax = 0
0046E108 54 PUSH ESP ; oldProtect
0046E109 6A 04 PUSH 0x4 ; PAGE_READWRITE
0046E10B 53 PUSH EBX ; 大小是0x1000
0046E10C 57 PUSH EDI ; 从基地址开始
0046E10D FFD5 CALL EBP
0046E10F 8D87 1F020000 LEA EAX,DWORD PTR DS:[EDI+0x21F] ; eax = 这个地址,暂时还知道是干什么的
0046E115 8020 7F AND BYTE PTR DS:[EAX],0x7F
0046E118 8060 28 7F AND BYTE PTR DS:[EAX+0x28],0x7F ; 修复.rerc的名称
0046E11C 58 POP EAX
0046E11D 50 PUSH EAX
0046E11E 54 PUSH ESP
0046E11F 50 PUSH EAX
0046E120 53 PUSH EBX
0046E121 57 PUSH EDI
0046E122 FFD5 CALL EBP ; 把内存属性改回来
0046E124 58 POP EAX
0046E125 61 POPAD
0046E126 8D4424 80 LEA EAX,DWORD PTR SS:[ESP-0x80]
0046E12A 6A 00 PUSH 0x0
0046E12C 39C4 CMP ESP,EAX
0046E12E ^ 75 FA JNZ SHORT 00_upx.0046E12A
0046E130 83EC 80 SUB ESP,-0x80
0046E133 ^ E9 DC68FEFF JMP 00_upx.00454A14
0046E138 50 PUSH EAX
0046E139 E1 46 LOOPDE SHORT 00_upx.0046E181
0046E13B 0060 E1 ADD BYTE PTR DS:[EAX-0x1F],AH
- 把upx1 中的数据拷贝到upx0中
- 恢复各个区段的数据
- 对call 和jmp 指令的地址进行解密
- 修复导入表
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!