某样本vmp分析 密级: 【C-1】 | 时间:2024-02-28 | 目录:测试笔记 | 编辑本文 上一版本 | 版本差异 | 下一版本 文章距今已发表三个月,请自行判断文中技术方法、代码的有效性:) ## 前言 以该分支为例,做分析学习记录 ## 样本伪代码 ``` switch ( vmcode >> 26 ) // opcode { case 1u: case 6u: v49 = *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2); v50 = *((unsigned int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2); v51 = (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); Reg_Context = (_QWORD *)sub_1A7708((_QWORD *)*Reg_Context); *(_WORD *)((char *)Reg_Context + v50 + v51) = v49; goto LABEL_103; case 4u: v43 = *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2); v44 = *((unsigned int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2); v45 = (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); Reg_Context = (_QWORD *)sub_1A7708((_QWORD *)*Reg_Context); *(_DWORD *)((char *)Reg_Context + v44 + v45) = v43; goto LABEL_103; case 5u: v37 = Reg_Context + 1; v2 = (vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1)); v3 = *((unsigned int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2); v4 = (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); Reg_Context = (_QWORD *)sub_1A7708((_QWORD *)*Reg_Context); *((_DWORD *)v37 + v2) = *(_DWORD *)((char *)Reg_Context + v3 + v4); goto LABEL_103; case 7u: v6 = *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2); v7 = *((unsigned int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2); v8 = (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); Reg_Context = (_QWORD *)sub_1A7708((_QWORD *)*Reg_Context); *((_BYTE *)Reg_Context + v7 + v8) = v6; goto LABEL_103; case 8u: if ( *((_DWORD *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2) != *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2) ) goto LABEL_103; goto LABEL_70; case 9u: v20 = Reg_Context + 1; v21 = HIWORD(vmcode) & 0x1F; v22 = *((_DWORD *)Reg_Context + ((vmcode >> 21) & 0x1F) + 2) + (__int16)vmcode; goto LABEL_2; case 10u: v18 = Reg_Context + 1; v28 = (vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1)); v29 = *((unsigned int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2); v30 = (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); Reg_Context = (_QWORD *)sub_1A7708((_QWORD *)*Reg_Context); *((_DWORD *)v18 + v28) = *(unsigned __int16 *)((char *)Reg_Context + v29 + v30); goto LABEL_103; case 11u: case 14u: v5 = Reg_Context + 1; v31 = (vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1)); v32 = *((unsigned int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2); v33 = (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); Reg_Context = (_QWORD *)sub_1A7708((_QWORD *)*Reg_Context); *((_DWORD *)v5 + v31) = *((unsigned __int8 *)Reg_Context + v32 + v33); goto LABEL_103; case 12u: v23 = *((unsigned int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2); v24 = *((_WORD *)Reg_Context + 2 * ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 5); v25 = (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); Reg_Context = (_QWORD *)sub_1A7708((_QWORD *)*Reg_Context); *(_WORD *)((char *)Reg_Context + v23 + v25 - 1) = v24; goto LABEL_103; case 13u: v53 = Reg_Context + 1; v54 = 4LL * ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))); *(_DWORD *)((char *)v53 + v54) = *(unsigned __int16 *)((char *)Reg_Context + v54 + 8); v38 = *((unsigned int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2); v39 = (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); Reg_Context = (_QWORD *)sub_1A7708((_QWORD *)*Reg_Context); *(_DWORD *)((char *)v53 + v54) |= *(unsigned __int16 *)((char *)Reg_Context + v38 + v39 - 1) << 16; goto LABEL_103; case 15u: v17 = HIWORD(vmcode) & 0x1F; v19 = vmcode << 16; goto LABEL_58; case 28u: if ( (vmcode & 0x7FF) == 2 ) *((_DWORD *)Reg_Context + ((unsigned __int16)vmcode >> 11) + 2) = *((_DWORD *)Reg_Context + ((vmcode >> 21) & 0x1F) + 2) * *((_DWORD *)Reg_Context + (HIWORD(vmcode) & 0x1F) + 2); goto LABEL_103; case 32u: *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2) = *((_DWORD *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2) ^ ((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0xFFFF07FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); goto LABEL_103; case 34u: *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2) = (vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0xFFFF07FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11) | *((_DWORD *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2); goto LABEL_103; case 35u: *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2) = *((_DWORD *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2) & ((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0xFFFF07FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); goto LABEL_103; case 36u: *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2) = *((_DWORD *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2) < (unsigned int)(__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); goto LABEL_103; case 37u: *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2) = *((_DWORD *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2) < (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); goto LABEL_103; case 38u: *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2) = *((_DWORD *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2) + (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); goto LABEL_103; case 40u: if ( *((int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2) < 1 ) goto LABEL_103; goto LABEL_70; case 41u: if ( *((int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2) > 0 ) goto LABEL_103; goto LABEL_70; case 42u: if ( *((_DWORD *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2) == *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2) ) goto LABEL_103; goto LABEL_70; case 43u: v46 = Reg_Context + 1; v47 = 4LL * ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))); *(_DWORD *)((char *)v46 + v47) = *(_DWORD *)((_BYTE *)Reg_Context + v47 + 8) & 0xFFFF0000; v26 = *((unsigned int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2); v27 = (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); Reg_Context = (_QWORD *)sub_1A7708((_QWORD *)*Reg_Context); *(_DWORD *)((char *)v46 + v47) |= *(unsigned __int16 *)((char *)Reg_Context + v26 + v27); goto LABEL_103; case 46u: if ( (*((_DWORD *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2) & 0x80000000) == 0 ) goto LABEL_103;LABEL_70: v40 = Reg_Context[18]; v52 = (int)(((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0xFFFF07FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)) << 16) >> 14; Reg_Context = sub_1A7B40(Reg_Context, *(_DWORD *)(v40 + 4)); v56[18] = v40 + v52; goto LABEL_103; case 58u: v35 = Reg_Context[18]; v36 = (int)(vmcode << 6) >> 4; *(_DWORD *)Reg_Context[36] = v35 + 8; Reg_Context = sub_1A7B40(Reg_Context, *(_DWORD *)(v35 + 4)); v42 = v35 + 4 + v36; goto LABEL_43; ``` ## vmcode结构分析 首先将vmcode右移26位,获取高位6比特作为opcode进入switch,从右到左进行索引,记录vmcode结构: instruction_fields = [ {"name": "OPCODE", "bits": 6, "position": 26, "is_opcode": True}, ] ![](https://img.meituan.net/imgupload/2033271e0873df64b1d22985cf481d5345106.png) case1和case6会进入同一个分支,单独做代码分析: ``` v49 = *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2); v50 = *((unsigned int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2); v51 = (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); Reg_Context = (_QWORD *)sub_1A7708((_QWORD *)*Reg_Context); *(_WORD *)((char *)Reg_Context + v50 + v51) = v49; ``` 逐行分析,v49由三部分组成,(_DWORD *)Reg_Context,((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))),+2 ``` v49 = *((_DWORD *)Reg_Context + ((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1))) + 2); ``` 可以理解成从Reg_Context越过两个字段后,取索引((vmcode >> 19) & 0xC | (vmcode >> 9) & 3 | (16 * ((vmcode >> 2) & 1)))位置的寄存器的值,这里我们把它标记为Rn。 Rn由vmcode上三部分操作码组成,记录一下怎么取的,首先是(vmcode >> 19) & 0xC: ¥¥¥¥ ¥¥¥¥ ¥AA¥ ¥¥¥¥ ¥¥¥¥ ¥¥¥¥ ¥¥¥¥ ¥¥¥¥ 然后是 (vmcode >> 9) & 3 ¥¥¥¥ ¥¥¥¥ ¥AA¥ ¥¥¥¥ ¥¥¥¥ ¥BB¥ ¥¥¥¥ ¥¥¥¥ 然后是 (vmcode >> 2) & 1 ¥¥¥¥ ¥¥¥¥ ¥AA¥ ¥¥¥¥ ¥¥¥¥ ¥BB¥ ¥¥¥¥ ¥C¥¥ 然后分析v50,同样从Reg_Context取某个寄存器的值,标记为Rm。 ``` v50 = *((unsigned int *)Reg_Context + ((vmcode >> 14) & 0xC | vmcode & 3 | (vmcode >> 4) & 0x10) + 2); ``` 同样记录下怎么取,首先是(vmcode >> 14) & 0xC: ¥¥¥¥ ¥¥¥¥ ¥AA¥ ¥¥DD ¥¥¥¥ ¥BB¥ ¥¥¥¥ ¥C¥¥ 然后是 vmcode & 3 ¥¥¥¥ ¥¥¥¥ ¥AA¥ ¥¥DD ¥¥¥¥ ¥BB¥ ¥¥¥¥ ¥CEE 最后是 (vmcode >> 4) & 0x10 ¥¥¥¥ ¥¥¥¥ ¥AA¥ ¥¥DD ¥¥¥¥ ¥BBF ¥¥¥¥ ¥CEE 最后看v51 ``` v51 = (__int16)((vmcode >> 18) & 0xE0 | ((unsigned __int16)vmcode >> 11) & 0x7FF | (vmcode >> 10) & 0x700 | ((unsigned __int8)vmcode >> 3 << 11)); ``` v51不是一个从context中取寄存器的操作了,是直接取了值。我们设置为imme,立即数。也是分为几个部分,逐个来看: (vmcode >> 18) & 0xE0 ¥¥¥¥ ¥¥GG GAA¥ ¥¥DD ¥¥¥¥ ¥BBF ¥¥¥¥ ¥CEE ((unsigned __int16)vmcode >> 11) & 0x7FF ,这里对vmcode强转int16,也就是舍掉高位16比特,然后再右移11位,取最低5位 ¥¥¥¥ ¥¥GG GAA¥ ¥¥DD HHHH HBBF ¥¥¥¥ ¥CEE (vmcode >> 10) & 0x700 ¥¥¥¥ ¥¥GG GAAJ JJDD HHHH HBBF ¥¥¥¥ ¥CEE (unsigned __int8)vmcode >> 3 << 11) 一样是获取最低8位然后右移,取值为: ¥¥¥¥ ¥¥GG GAAJ JJDD HHHH HBBF KKKK KCEE 我们发现vmcode取完了,前六位是opcode。 abc ->Rn def ->Rm ghjk->imme 写一下字段分布 instruction_fields = [ {"name": "Rm1", "bits": 2, "position": 0, "is_opcode": False}, {"name": "Rn1", "bits": 1, "position": 2, "is_opcode": False}, {"name": "imme1", "bits": 5, "position": 3, "is_opcode": False}, {"name": "Rm2", "bits": 1, "position": 8, "is_opcode": False}, {"name": "Rn2", "bits": 2, "position": 9, "is_opcode": False}, {"name": "imme2", "bits": 5, "position": 11, "is_opcode": False}, {"name": "Rm3", "bits": 2, "position": 16, "is_opcode": False}, {"name": "imme3", "bits": 3, "position": 18, "is_opcode": False}, {"name": "Rn3", "bits": 2, "position": 21, "is_opcode": False}, {"name": "imme4", "bits": 3, "position": 23, "is_opcode": False}, {"name": "OPCODE", "bits": 6, "position": 26, "is_opcode": True}, ] 由此可生成vmcode的结构图,如下(忽略命名不一致,自行对照映射): ![](https://img.meituan.net/imgupload/39e2d70dc61e7ec039c3984be37b744b33558.png) ## 逻辑分析 ### case1、6 回到case1、6的分支代码,取完Rn Rm后,做了如下操作: Reg_Context = (_QWORD *)sub_1A7708((_QWORD *)*Reg_Context); 将Reg_Context值转为一个64位的Quad WORD(四字),通常用于表示64位的整数,然后进入函数做了: *a1 & 0xFFFFFFFF00000000LL; 最终Reg_Context的值就是0。然后做了 *(_WORD *)((char *)Reg_Context + v50 + v51) = v49; v49是Rn,v50是Rm,v51是imme,效果等同于 STR Rn,[Rm,#imme],查看汇编 ![](https://img.meituan.net/imgupload/aeec0e979640b6722e471ae96e098fbe15156.png) 所以case1、6等价于 STRH Value [Base, #offset] ### case4 查看case4 ![](https://img.meituan.net/imgupload/9fe4a991806b42dff28e475fe0d4abe141947.png) 查看汇编 ![](https://img.meituan.net/imgupload/9d946ef7430286986fa7382ba7e96a498092.png) 所以case4等价于 STRW Value [Base, #offset] ### case5 查看case5 ![](https://img.meituan.net/imgupload/c5b70ae6113190bf6d3086664a2060c641725.png) 首先给Reg_context加了8字节,等价于之前的+2。 此时做的操作*((_DWORD *)v37 + v2) = *(_DWORD *)((char *)Reg_Context + v3 + v4);,等价于 LDR Value [Base, #offset] 查看汇编,注意v2等价之前的索引值。 ![](https://img.meituan.net/imgupload/5c1776ea38b1ee9945c888b3053db41f10953.png) 所以case5等价于 LDRW Value [Base, #offset] ### case7 同理 等价于 STRB Value [Base, #offset] ### case8 ![](https://img.meituan.net/imgupload/56e4265ba7a21e4161fe39fa65572e0b21341.png) 将Rn与Rm进行比较 等价于CMP ### case9 这里就比较特殊了,首先从vmcode.0x10位置取了宽度为5个位,然后取五位。 也就是Rd ¥¥¥¥ ¥¥¥¥ ¥¥¥A AAAA ¥¥¥¥ ¥¥¥¥ ¥¥¥¥ ¥¥¥¥ v22 = *((_DWORD *)Reg_Context + ((vmcode >> 21) & 0x1F) + 2) + (__int16)vmcode 这行代码也是拿到一个寄存器的值Rn,然后加上一个立即数imme。 ¥¥¥¥ ¥¥¥¥ ¥¥¥A AAAA BBBB BBBB BBBB BBBB ¥¥¥¥ ¥¥CC CCCA AAAA BBBB BBBB BBBB BBBB instruction_fields = [ {"name": "imme", "bits": 16, "position": 0, "is_opcode": False}, {"name": "Rd", "bits": 5, "position": 16, "is_opcode": False}, {"name": "Rn", "bits": 5, "position": 21, "is_opcode": False}, {"name": "OPCODE", "bits": 6, "position": 26, "is_opcode": True}, ] 评论列表 写评论 您的IP:3.15.0.120,临时用户名:f9241d20评论已接入DepyWAF审计与流量系统,请勿频繁操作导致IP拉黑 提交评论 © 版权声明:非标注『转载』情况下本文为原创文章,版权归 Depy's docs 所有,转载请联系博主获得授权。