跳至主要内容

UPX 脱壳之upx壳部分的代码分析

http://ift.tt/2AmVQ0n
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壳主要的执行流程,脱还是很好脱的

The post UPX 脱壳之upx壳部分的代码分析 appeared first on cole.

http://ift.tt/eA8V8J reverse engineering, decrypt, reverse, software encrypt, unpack December 08, 2017 at 02:57PM

评论

此博客中的热门博文

反Hook 之自己实现GetProcAddress和LoadLibraryA(c++ version)

http://ift.tt/2AocAD0 // 课上练习.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> //要实现的功能: /* 自己实现GerProcAddress LoadLibraryA */ //思路: /* GerProcAddress和LoadLibraryA都是在Kernel32.dll中的 首要的任务就是找到Kernel32.dll,然后遍历IAT就能找到这两个函数 现在首要的难点就是如何找到kernel32.dll() 通过dll的加载顺序可以找到,而且虽然需要使用未文档化的API,但是好在windgb可以直接看,而且用到的也并不是很多 懒得去晚上搜了,直接自己实现一个得了,作为演示,能用就行(最好还是搜一个正规的结构体) */ /* 0: kd> dt _TEB /a nt!_TEB +0x000 NtTib : _NT_TIB +0x01c EnvironmentPointer : Ptr32 Void +0x020 ClientId : _CLIENT_ID +0x028 ActiveRpcHandle : Ptr32 Void +0x02c ThreadLocalStoragePointer : Ptr32 Void +0x030 ProcessEnvironmentBlock : Ptr32 _PEB //首先实现部分TEB结构体,因为只需要用的PEB的部分,所以实现到这就ok了 */ //构建 TEB typedef struct _TEB { //保证0x30是peb的结构体就ok了 struct _tem { DWORD a1; DWORD a2; DWORD a3; DWORD a4; DWORD a5; DWORD a6; DWORD a7; DWORD a8; DWORD a9; DWORD a10; ...

IDA动态调试ELF中遇到的问题(1)

https://ift.tt/2Gxnf2F 遇到 got SIGCHLD singal(child status has changed)...这种提示 singal fork了子进程 直接点yes,然后继续单步执行,出来提示 pass to application就行了 内存查看 用od习惯了之后,凡事都想右键看一下内存,但是IDA中好像没有那么如意, 目前只是在栈中右键发现有查看hex的选项 动态调试的时候的nop ida保存动态调试的时候修改的数据 方法一:在程序运行的时候保存 The post IDA动态调试ELF中遇到的问题(1) appeared first on cole . https://ift.tt/2q9Qf5g WHATEVER April 05, 2018 at 09:44AM

数据库(MySQL)编程之数据库和表的基本操作

http://ift.tt/2gOXg6X     数据库和表的基本操作 操作前的基本知识 基本 sql语句不区分大小写(关键字建议用大小写),但字符串常量区分大小写 sql语句可单行或多行书写,以; 结尾 关键字不能跨行或简写 可以用空格或者缩进来提高可读性 注释 sql标准 /**/ : 多行注释 "--":单行注释 mysql 标准: "#":单行注释 "COMMENT":为字段或列添加注释 创建和查看数据库 创建数据库 模板 CREATE DATABASE [IF NOT EXISTS ] db_name create_specification: IF NOT EXITSTS : 检查数据库是否存在,如果存在就不创建 creat_specification :创建条件 CHARACTER SET: 制定数据聚采用的字符集 COLLATE :制定数据库字符集的比较方式 DEFAULT :表示默认内容,即使没有制定该项,也有默认的值 # 创建数据库使用字符集为 UTF-8 ,使用规则为 uftf_bin CREATE DATABASE db_name DEFAULT CHARACTER SET utf8 COLLATE uft8_bin; 一般默认的数据库就是utf8 和utf8_bin的比较方式,因此一般创建数据库的时候都用 CREATE DATABASE db_name mysql> create database testdb; Query OK, 1 row affected (0.02 sec) 查看数据库 命令 SHOW DATABASES     mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sakila | | sys | | testdb | |...