Table of Contents
ezvm
输入第一串字符串,生成新的 check.dll,然后用新的 check.dll 的 check 函数来验证 flag。
这个 vm 的流程大概是:
#include <stdio.h>
int main(){ static int stack[] = {162, 0,132,163,8,0,163,8,1,176, 8, 316, 178, 163, 9, 1, 163, 9, 2, 163, 9, 3, 176, 9, 158, 178, 166, 4, 22, 163, 0, 4, 176, 0, 889, 178, 164, 5, 11, 161, 8, 5, 163, 8, 6, 176, 8, 38, 178, 163, 7, 6, 176, 7, 96, 178, 161, 9, 1, 163, 9, 2, 165, 9, 5, 176, 9, 111, 178, 166, 5, 7, 161, 8, 0, 165, 8, 6, 163, 8, 5, 176, 8, 859, 178, 163, 3, 4, 176, 3, 706, 178, 192}; int memory[8] = {}; int out = 0; int i; while(i < 92){ switch(stack[i]){ case 160: printf("mov [%d], %d\n", stack[i+1], stack[i+2]); i += 3; break; case 161 : printf("mov [%d], [%d]\n", stack[i+1], stack[i+2]); i += 3; break; case 162 : printf("add [%d], %d\n", stack[i+1], stack[i+2]); i += 3; break; case 163 : printf("add [%d], [%d]\n", stack[i+1], stack[i+2]); i += 3; break; case 164 : printf("sub [%d], %d\n", stack[i+1], stack[i+2]); i += 3; break; case 165 : printf("sub [%d], [%d]\n", stack[i+1], stack[i+2]); i += 3; break; case 166 : printf("mul [%d], %d\n", stack[i+1], stack[i+2]); i += 3; break; case 167 : printf("mul [%d], [%d]\n", stack[i+1], stack[i+2]); i += 3; break; case 176 : printf("cmp [%d], %d\n", stack[i+1], stack[i+2]); i += 3; // out = 1; break; case 177 : printf("cmp [%d], [%d]\n", stack[i+1], stack[i+2]); i += 3; // out = 1; break; case 178 : printf("jnz end\n"); if(out != 0){ goto ending; } i++; break; case 192: goto ending; default: break; }
} ending: return 0;}得到:
add [0], 132add [8], [0]add [8], [1]cmp [8], 316jnz endadd [9], [1]add [9], [2]add [9], [3]cmp [9], 158jnz endmul [4], 22add [0], [4]cmp [0], 889jnz endsub [5], 11mov [8], [5]add [8], [6]cmp [8], 38jnz endadd [7], [6]cmp [7], 96jnz endmov [9], [1]add [9], [2]sub [9], [5]cmp [9], 111jnz endmul [5], 7mov [8], [0]sub [8], [6]add [8], [5]cmp [8], 859jnz endadd [3], [4]cmp [3], 706jnz end用 z3 求解(真难写啊…)
from z3 import *
x = [Int(f'x{i}') for i in range(8)]
s = Solver()for i in range(8): s.add(x[i] < 100)
s.add(x[0] + x[1] + 132 == 316)s.add(x[1] + x[2] + x[3] == 158)s.add(x[4] * 22 + x[0] + 132== 889)s.add(x[5] - 11 + x[6] == 38)s.add(x[7] + x[6]== 96)s.add(x[1] + x[2] - x[5] + 11== 111)s.add((x[0] + 132 + x[4]*22) - x[6] + (x[5]-11)*7 == 859)s.add(x[3] + x[4]*22== 706)
print(s.check() )for i in x: print(s.model().evaluate(i), end="")字符串 1 为 9787254630123759
根据新的 check 函数,可以得到 flag
#include <stdio.h>
int main(){ char tmp[44] = { 0x0D, 0x08, 0x1A, 0x0A, 0x1D, 0x0F, 0x32, 0x78, 0x2A, 0x7B, 0x2A, 0x7B, 0x7C, 0x7D, 0x71, 0x64, 0x7A, 0x2C, 0x7B, 0x7D, 0x64, 0x28, 0x7D, 0x71, 0x2C, 0x64, 0x78, 0x78, 0x7D, 0x7A, 0x64, 0x28, 0x7A, 0x7D, 0x70, 0x7F, 0x28, 0x7A, 0x2B, 0x7E, 0x7D, 0x79, 0x79, 0x34};
for(int i = 0; i < 44; i++){ tmp[i] ^= 0x49; }
printf("%s\n", tmp);}flag DASCTF{1c2c2548-3e24-a48e-1143-a3496a3b7400}
unwind
control E 查看入口函数
result = ReadProcessMemory(hProcess, lpBaseAddress, &unk_96B30C, nSize, NumberOfBytesRead); if ( result ) return WriteProcessMemory(hProcess, (LPVOID)lpBaseAddress, &byte_96B31C, nSize, NumberOfBytesRead);把下面这个内容
变成这个
点进去是一个 unwind
下面触发 try except,int 3 之后跳转到 0x962079
#include <stdio.h>#define uint unsigned int#define MX ((z ^ k[e ^ i & 3]) + (y ^ sum)) ^ (((16 * z) ^ (y >> 3)) + ((4 * y) ^ (z >> 5)))
// 4 bytes a groupvoid xxtea(uint *v, int n, uint *k){ uint delta = 1640531527; uint t = 6 + 52/n; uint sum = -t*delta; uint y = v[0]; uint e, z, i;
while(t--){ e = (sum>>2) & 3; for(i = n - 1; i > 0; --i){ z = v[i - 1]; y = v[i] -= MX; } z = v[n - 1]; y = v[0] -= MX; sum += delta; }}
void tea(uint n, uint *v, uint *key){ uint l = v[0], r = v[1], sum = 0, delta = 1640531527; sum = -delta * n; for (int i = 1; i <= n; ++i) { r -= (key[(sum >> 11) & 3] + sum) ^ (l + ((l >> 5) ^ (16 * l))); sum += delta; l -= (key[sum & 3] + sum) ^ (r + ((r >> 5) ^ (16 * r))); } v[0] = l; v[1] = r;}
int main(){ uint v[8] = {0x87AAA7C1, 0x857321B6, 0x0E71D28C, 0xCADF39F2, 0x58EFCA14, 0xD7E7D9D8, 0xF29F5C5D, 0x5F5ED45E}; uint k[] = {0x44, 0x41, 0x53, 0x21}; for(int i = 0; i <4; i++){ tea(0x24, &v[i*2], k); // n = 0x24 } for(int i = 0; i <4; i++){ tea(0x24, &v[i*2], k); // n = 0x24 } xxtea(v, 8, k); printf("%s", (char *)v);}为啥 unwind 会把 tea 执行两次啊???
prese
用 d810 来去 ollvm 平坦化。然后就变得好看了。
写脚本发现结果不对。接着在 crypto_flag 变量这里看见 anti() 函数
.data:00007FF64D4A0010 crypto_flag db 86h ; DATA XREF: anti(void)+2DD↑o.data:00007FF64D4A0010 ; anti(void)+309↑o.data:00007FF64D4A0010 ; main+275↑o原来是 GLOBAL__sub_I_prese_cpp 这里藏了个 anti 函数异或 0x22。(GLOBAL__sub_I_prese_cpp 一般clang 用来初始化全局变量的)
#include <stdio.h>
void decry(int input_len, char* input){ for(int i = 0; i < input_len; i++){ input[i] = (~(input[i]^0x22)) ^ input_len; }}
int main(){ unsigned char input[31] = { 0x86, 0x83, 0x91, 0x81, 0x96, 0x84, 0xB9, 0xA5, 0xAD, 0xAD, 0xA6, 0x9D, 0xB6, 0xAA, 0xA7, 0x9D, 0xB0, 0xA7, 0x9D, 0xAB, 0xB1, 0x9D, 0xA7, 0xA3, 0xB1, 0xBB, 0xAA, 0xAA, 0xAA, 0xAA, 0xBF}; decry(31, input); printf("%31s\n", input);}