从 js 中看出,先把输入的 flag 存到 wasm 的内存为 0 的位置,然后调用 vault.wasm 导出的 unlock() 函数判断
这里用开源项目 wabt 把 wasm 转成 c 文件,再用 gcc 编译成二进制文件,这样就可以拖到 ida 里分析了
wasm2c vault.wasm -o vault.c
gcc -s vault.c -o vault.o
ida里看到,里面用了很多 w2c_env_x() 混淆,这个函数在 js 里。简单分析可知,对于一个常数,函数的返回结果是固定的
为了去除 w2c_env_x() 的混淆,可以用 mov eax,0/1 替换 call w2c_env_x,刚好都是5个字节
import idautils
import ida_idaapi
import ida_bytes
import idc
import idaapi
import ctypesdef x(n):r = 0while n!=0:r^=(n&1)n>>=1return rfunc_x_addr = 0x0000000000007988for addr in idautils.Functions():if 'w2c_vault' in idc.get_func_name(addr):for ea in idautils.FuncItems(addr):insn = idaapi.insn_t()if idaapi.decode_insn(insn, ea) == 0:continue# call w2c_env_xif insn.itype == idaapi.NN_call and insn.ops[0].addr == func_x_addr:result = 0# 找到传参指令 movmov_ea = eafor i in range(6): mov_ea = idc.prev_head(mov_ea)insn = idaapi.insn_t()idaapi.decode_insn(insn, mov_ea)if insn.itype == idaapi.NN_mov and insn.ops[0].type == idaapi.o_displ and insn.ops[1].type == idaapi.o_imm:result = x(insn.ops[1].value)else:raise Exception('error')ida_bytes.patch_byte(ea, 0xb8) # mov eaxida_bytes.patch_dword(ea+1, result) # 0/1
去混淆前
去混淆后
接下来花了亿点时间分析。它根据输入的 flag 构建了一个 Huffman 树,flag 每个字符用根到叶子节点的路径编码表示。
最后把序列化后的 Huffman 树和 flag 的编码和一个固定的字符串比较。
#include <stdio.h>
#include <stdint.h>uint8_t exported_data[] = {0xB5, 0x03, 0x00, 0x00, 0xE8, 0xC6, 0x66, 0x0C, 0xD7, 0xC1, 0xC7, 0x64, 0x9D, 0x11, 0x1C, 0xBE, 0x12, 0x75, 0x58, 0xCA, 0x6E, 0x00, 0x4E, 0x4C, 0x45, 0x2D, 0xA4, 0x46, 0x89, 0x8C, 0xD5, 0x65, 0x35, 0xBB, 0x9B, 0xC2, 0xCB, 0xEB, 0x36, 0x30, 0xB5, 0x90, 0x2A, 0xAA, 0x35, 0x44, 0xD1, 0xDC, 0xBA, 0xB8, 0x05, 0x61, 0x5A, 0xFD, 0xF9, 0x6B, 0x6F, 0xCB, 0x5B, 0x7E, 0x5A, 0xDA, 0xBE, 0xF4, 0xB6, 0x0F, 0xEB, 0x17, 0x05, 0x45, 0xB0, 0x47, 0xF3, 0x4A, 0x17, 0xF3, 0x71, 0x11, 0xDA, 0x5A, 0x2B, 0x86, 0xEA, 0x79, 0xEB, 0x1A, 0xA2, 0xEC, 0x17, 0xA1, 0x0B, 0x83, 0x79, 0x6D, 0xD4, 0xF3, 0xDF, 0x96, 0x5B, 0x57, 0x41, 0x7F, 0x4E, 0xE7, 0x68, 0xE9, 0x8F, 0x48, 0x41, 0x77, 0x0E, 0x1B, 0x9F, 0x1A, 0xAD, 0x3E, 0xF8, 0xA4, 0x89, 0xD3, 0x63, 0x52, 0x40, 0xB8, 0xAE, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};typedef struct _node {uint8_t value;uint8_t freq;struct _node* lchild;struct _node* rchild;
} node;node tree[256] = {0};
uint32_t node_cnt = 0;node* build_tree(uint8_t* ptr, uint32_t bit, uint32_t node_idx, uint32_t* next_bit) {uint32_t byte = bit / 8;uint32_t offset = 7 - bit % 8;if( (ptr[byte] >> offset) & 1){uint32_t next1, next2;tree[node_idx].lchild = build_tree(ptr, bit+1, ++node_cnt, &next1);tree[node_idx].rchild = build_tree(ptr, next1, ++node_cnt, &next2);*next_bit = next2;}else{uint8_t val = 0;for(int i=8;i>=1;i--){byte = (bit + i) / 8;offset = 7 - (bit + i) % 8;val = (val<<1) + ((ptr[byte] >> offset) & 1);}tree[node_idx].lchild = NULL;tree[node_idx].rchild = NULL;tree[node_idx].value = val;tree[node_idx].freq = 0;*next_bit = bit + 8 + 1;}return &tree[node_idx];
}char prefix[256];
uint32_t prefix_len=0;
void print_tree(node *nd, uint32_t depth, uint32_t is_right)
{if(depth>0){prefix[prefix_len] = 0;printf("%s|-", prefix);prefix[prefix_len++] = is_right ? ' ':'|';prefix[prefix_len++] = ' ';prefix[prefix_len] = 0;}if (nd->lchild || nd->rchild){printf("\\\n");print_tree(nd->lchild, depth+1, 0);print_tree(nd->rchild, depth+1, 1);}else{printf("'%c'\n",nd->value);}if(depth>0)prefix_len -= 2;}void get_flag(uint8_t* ptr, node* tree_root, uint16_t bit_start, uint16_t bit_limit)
{uint32_t byte;uint32_t offset;node *nd = tree_root;for (uint32_t bit = bit_start; bit <= bit_limit; bit++){byte = bit / 8;offset = 7 - bit % 8;if((ptr[byte] >> offset) & 1)nd = nd->rchild;elsend = nd->lchild;if(!nd->lchild && !nd->rchild){printf("%c",nd->value);nd = tree_root;}}
}int main()
{uint32_t bit_stream_len = *(uint32_t*)&exported_data[0];uint8_t* data_ptr = &exported_data[4];uint32_t start_bit;node* root = build_tree(data_ptr, 0, 0, &start_bit);
// printf("tree built\n");// print_tree(root, 0, 0);get_flag(data_ptr, root, start_bit, bit_stream_len);
}
flag: crew{7H15_15_4_V3rY_V3rY_V3rY_5UP3r_1NCr3D181Y_10N6_F146_600D_J08_1_C0MPr3553D_17_W17H_MY_C0MPU73r_5C13NC3_6C53_KN0W13D63_1_6U355_f4c91dbe}