欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

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

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

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壳主要的执行流程,脱还是很好脱的

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功