DASCTFxGFCTF ezvm&unwind&prese

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], 132
add [8], [0]
add [8], [1]
cmp [8], 316
jnz end
add [9], [1]
add [9], [2]
add [9], [3]
cmp [9], 158
jnz end
mul [4], 22
add [0], [4]
cmp [0], 889
jnz end
sub [5], 11
mov [8], [5]
add [8], [6]
cmp [8], 38
jnz end
add [7], [6]
cmp [7], 96
jnz end
mov [9], [1]
add [9], [2]
sub [9], [5]
cmp [9], 111
jnz end
mul [5], 7
mov [8], [0]
sub [8], [6]
add [8], [5]
cmp [8], 859
jnz end
add [3], [4]
cmp [3], 706
jnz 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);

把下面这个内容

image-20240425200607241

变成这个

image-20240425200951627

点进去是一个 unwind

image-20240425202122859

下面触发 try except,int 3 之后跳转到 0x962079

image-20240425174237245

#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 group
void 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);
}
posted @ 2024-05-01 19:03  giacomo捏  阅读(42)  评论(0编辑  收藏  举报