1

[原创]UPX 脱壳之upx壳部分的代码分析

八岛 2018-1-12 21:29 423

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

通过上面的分析可以看出,UPX用了部分的代码混淆,不过最后的执行流程是:

  • 把upx1 中的数据拷贝到upx0中
  • 恢复各个区段的数据
  • 对call 和jmp 指令的地址进行解密
  • 修复导入表

以上就是upx壳主要的执行流程,脱还是很好脱的

最新回复 (3)
8
kanxue 2018-1-12 22:07
3
优秀鼓励一下~
可以进一步分析,写出静态脱壳机
聖blue 2018-1-12 22:49
4
Diabloking 2018-1-13 00:04
5
那不叫解密,  叫解压,  纯粹的压缩壳
返回