K3RN3L CTF 2021 Rev - `Recurso & Rasm ` writeups




Our Team zh3ro ended up at 9 th position in `K3RN3LCTF - 2021`.
These Reverse engineering challs are interesting
-> Both are based on one vm only (Recurso), different compiled scripts . (leFlag.recc , rasm.recc)

Recurso

Given a Recurso Execuatable which can compile, run recurso sccrips.
And a leFlag.recc (a recurso compiled file) containing a flag checking logic, can be run by given Recurso executable.
The Goal of the challenge is to get the flag checking logic in leFlag.recc and steal flag

Decompile and Analyse -> Recurso

First we have to get to know about how the Recurso is running this byte code in order to get the logic of leFlag.recc
Decompiled the Code, changed the variable names based on code execuation, to ease my work.
Main function :
int __cdecl main(int argc, const char **argv, const char **envp)
{
const char *v3; // rax
void *v5; // [rsp+18h] [rbp-18h]
__int64 v6; // [rsp+20h] [rbp-10h]
int *v7; // [rsp+28h] [rbp-8h]
if ( argc > 1 && strstr(argv[1], ".recc") )
{
v7 = initBytecodeFileWithFile(argv[1]);
runProgram(*((_QWORD *)v7 + 2), *((_QWORD *)v7 + 3));
}
else if ( argc > 1 && strstr(argv[1], ".rec") )
{
fileData = (void *)readFile(argv[1], &fileSize);
v5 = fileData;
program = (__int64)initProgramNode();
v6 = initFunctionNode("main");
addElementToProgramNode(program, v6);
curFunction = v6;
parseFunction(program, &v5);
if ( argc == 4 && !strcmp(argv[2], "-s") )
v3 = argv[3];
else
v3 = (const char *)&unk_55B6B934B008;
compileBytecode(program, v3);
free(fileData);
freeProgramNode(program);
}
return 0;
}
view raw main.c hosted with ❤ by GitHub

runProgram is the Function we need to focus
__int64 __fastcall runProgram(__int64 a1, __int64 code)
{
int v2; // eax
int v3; // eax
int v4; // eax
int v5; // ecx
int v6; // eax
int v7; // eax
int v8; // eax
int v9; // eax
int v10; // eax
int v11; // eax
int v12; // eax
int v13; // eax
int v14; // eax
int v15; // eax
int v16; // eax
int v17; // eax
int v18; // ebx
int v19; // eax
int v20; // eax
int v21; // eax
int v22; // eax
__int64 v23; // rbx
int v24; // eax
int v25; // eax
int v26; // eax
int v27; // eax
int v28; // eax
int v29; // ecx
__int64 v30; // rax
int v31; // ebx
int v32; // eax
int v33; // eax
int v34; // eax
int v35; // eax
void **v36; // rbx
int v37; // eax
int v38; // eax
int v39; // eax
int v40; // eax
int v41; // eax
int v42; // eax
int v43; // eax
int v44; // eax
int v45; // eax
int v46; // eax
int v47; // eax
int v48; // eax
int v49; // eax
__int64 v50; // rcx
int v51; // eax
int v52; // eax
int v53; // eax
int v54; // eax
__int64 v55; // rcx
int v56; // eax
int v57; // eax
int v58; // eax
int v59; // eax
int v60; // ebx
int v61; // ecx
int v62; // eax
int v63; // eax
int v64; // eax
int v65; // eax
int v66; // eax
__int64 result; // rax
__int64 input_lld; // [rsp+28h] [rbp-138F8h]
char v69; // [rsp+34h] [rbp-138ECh]
char v70; // [rsp+35h] [rbp-138EBh]
char v71; // [rsp+36h] [rbp-138EAh]
char v72; // [rsp+37h] [rbp-138E9h]
char v73; // [rsp+38h] [rbp-138E8h]
char v74; // [rsp+39h] [rbp-138E7h]
char v75; // [rsp+3Ah] [rbp-138E6h]
char v76; // [rsp+3Bh] [rbp-138E5h]
char v77; // [rsp+3Ch] [rbp-138E4h]
char v78; // [rsp+3Dh] [rbp-138E3h]
char v79; // [rsp+3Eh] [rbp-138E2h]
char v80; // [rsp+3Fh] [rbp-138E1h]
__int64 stack[10001]; // [rsp+40h] [rbp-138E0h]
__int128 v82; // [rsp+138C8h] [rbp-58h]
char v83; // [rsp+138DFh] [rbp-41h]
_QWORD *funcs_ptr; // [rsp+138E0h] [rbp-40h]
int j; // [rsp+138E8h] [rbp-38h]
int i; // [rsp+138ECh] [rbp-34h]
void *call_stack; // [rsp+138F0h] [rbp-30h]
int v88_64; // [rsp+138FCh] [rbp-24h]
int cur_func; // [rsp+13900h] [rbp-20h]
int v90; // [rsp+13904h] [rbp-1Ch]
int v91; // [rsp+13908h] [rbp-18h]
int pc; // [rsp+1390Ch] [rbp-14h]
funcs_ptr = initializeFunctions((char *)a1);
pc = 0;
v91 = 0;
v90 = 0;
cur_func = 0;
v88_64 = 64;
call_stack = malloc(0x200uLL);
for ( i = 0; i < v88_64; ++i )
*((_QWORD *)call_stack + i) = malloc(8uLL);
while ( 1 )
{
result = *(unsigned __int8 *)(pc + code);
if ( (_BYTE)result == 13 )
return result;
v2 = pc++;
v83 = *(_BYTE *)(v2 + code);
switch ( v83 )
{
case 1:
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
v3 = v91++;
stack[v3] = *((_QWORD *)&v82 + 1) - v82;
break;
case 2:
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
v4 = v91++;
stack[v4] = v82 * *((_QWORD *)&v82 + 1);
break;
case 3:
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
v6 = v91++;
stack[v6] = *((_QWORD *)&v82 + 1) + v82;
break;
case 8:
v10 = pc++;
v73 = *(_BYTE *)(v10 + code);
v11 = pc++;
v74 = *(_BYTE *)(v11 + code);
v12 = pc++;
v75 = *(_BYTE *)(v12 + code);
v13 = pc++;
v76 = *(_BYTE *)(v13 + code);
v14 = pc++;
v77 = *(_BYTE *)(v14 + code);
v15 = pc++;
v78 = *(_BYTE *)(v15 + code);
v16 = pc++;
v79 = *(_BYTE *)(v16 + code);
v17 = pc++;
v80 = *(_BYTE *)(v17 + code);
v18 = v91++;
stack[v18] = bytesToLongLong((unsigned __int8 *)&v73);
break;
case 0xB:
v19 = pc++;
v69 = *(_BYTE *)(v19 + code);
v20 = pc++;
v70 = *(_BYTE *)(v20 + code);
v21 = pc++;
v71 = *(_BYTE *)(v21 + code);
v22 = pc++;
v72 = *(_BYTE *)(v22 + code);
v23 = stack[--v91];
v24 = bytesToInt((unsigned __int8 *)&v69);
setLocal(funcs_ptr[cur_func], v24, v23);
break;
case 0xC:
v25 = pc++;
v69 = *(_BYTE *)(v25 + code);
v26 = pc++;
v70 = *(_BYTE *)(v26 + code);
v27 = pc++;
v71 = *(_BYTE *)(v27 + code);
v28 = pc++;
v72 = *(_BYTE *)(v28 + code);
v29 = bytesToInt((unsigned __int8 *)&v69);
v30 = funcs_ptr[cur_func];
v31 = v91++;
stack[v31] = getLocal(v30, v29);
break;
case 0xE:
decrementFunction(funcs_ptr[cur_func]);
pc = **((_DWORD **)call_stack + --v90);
cur_func = *(_DWORD *)(*((_QWORD *)call_stack + v90) + 4LL);
break;
case 0xF:
printf("%lld\n", stack[--v91]);
break;
case 0x10:
--v91;
break;
case 0x13:
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
v5 = v91++;
stack[v5] = *((_QWORD *)&v82 + 1) / (__int64)v82;
break;
case 0x15:
v32 = pc++;
v69 = *(_BYTE *)(v32 + code);
v33 = pc++;
v70 = *(_BYTE *)(v33 + code);
v34 = pc++;
v71 = *(_BYTE *)(v34 + code);
v35 = pc++;
v72 = *(_BYTE *)(v35 + code);
if ( v90 >= v88_64 )
{
v88_64 *= 2;
call_stack = realloc(call_stack, 8LL * v88_64);
for ( j = v90; j < v88_64; ++j )
{
v36 = (void **)((char *)call_stack + 8 * j);
*v36 = malloc(8uLL);
}
}
*(_DWORD *)(*((_QWORD *)call_stack + v90) + 4LL) = cur_func;
**((_DWORD **)call_stack + v90++) = pc;
cur_func = bytesToInt((unsigned __int8 *)&v69);
pc = *(_DWORD *)funcs_ptr[cur_func];
incrementFunction(funcs_ptr[cur_func]);
break;
case 0x16:
__isoc99_scanf("%lld", &input_lld);
v37 = v91++;
stack[v37] = input_lld;
break;
case 0x17:
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
if ( *((_QWORD *)&v82 + 1) != (_QWORD)v82 )
{
do
{
v38 = pc++;
v83 = *(_BYTE *)(v38 + code);
}
while ( v83 != 14 );
}
break;
case 0x18:
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
v7 = v91++;
stack[v7] = v82 | *((_QWORD *)&v82 + 1);
break;
case 0x19:
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
v8 = v91++;
stack[v8] = v82 & *((_QWORD *)&v82 + 1);
break;
case 0x1A:
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
v9 = v91++;
stack[v9] = v82 ^ *((_QWORD *)&v82 + 1);
break;
case 0x1B:
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
if ( *((_QWORD *)&v82 + 1) == (_QWORD)v82 )
{
do
{
v39 = pc++;
v83 = *(_BYTE *)(v39 + code);
}
while ( v83 != 14 );
}
break;
case 0x1C:
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
if ( *((_QWORD *)&v82 + 1) < (__int64)v82 )
{
do
{
v40 = pc++;
v83 = *(_BYTE *)(v40 + code);
}
while ( v83 != 14 );
}
break;
case 0x1D:
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
if ( *((_QWORD *)&v82 + 1) > (__int64)v82 )
{
do
{
v41 = pc++;
v83 = *(_BYTE *)(v41 + code);
}
while ( v83 != 14 );
}
break;
case 0x1E:
v42 = pc++;
v69 = *(_BYTE *)(v42 + code);
v43 = pc++;
v70 = *(_BYTE *)(v43 + code);
v44 = pc++;
v71 = *(_BYTE *)(v44 + code);
v45 = pc++;
v72 = *(_BYTE *)(v45 + code);
pc = bytesToInt((unsigned __int8 *)&v69);
break;
case 0x1F:
pc = stack[--v91];
break;
case 0x20:
v46 = pc++;
v69 = *(_BYTE *)(v46 + code);
v47 = pc++;
v70 = *(_BYTE *)(v47 + code);
v48 = pc++;
v71 = *(_BYTE *)(v48 + code);
v49 = pc++;
v72 = *(_BYTE *)(v49 + code);
v50 = (int)bytesToInt((unsigned __int8 *)&v69);
++*(_BYTE *)(v50 + code);
break;
case 0x21:
v51 = pc++;
v69 = *(_BYTE *)(v51 + code);
v52 = pc++;
v70 = *(_BYTE *)(v52 + code);
v53 = pc++;
v71 = *(_BYTE *)(v53 + code);
v54 = pc++;
v72 = *(_BYTE *)(v54 + code);
v55 = (int)bytesToInt((unsigned __int8 *)&v69);
--*(_BYTE *)(v55 + code);
break;
case 0x22:
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
*(_BYTE *)(v82 + code) = *(_BYTE *)(*((_QWORD *)&v82 + 1) + code);
break;
case 0x23:
v56 = pc++;
v69 = *(_BYTE *)(v56 + code);
v57 = pc++;
v70 = *(_BYTE *)(v57 + code);
v58 = pc++;
v71 = *(_BYTE *)(v58 + code);
v59 = pc++;
v72 = *(_BYTE *)(v59 + code);
v60 = *(unsigned __int8 *)((int)bytesToInt((unsigned __int8 *)&v69) + code) << 8;
v61 = v60 + *(unsigned __int8 *)((int)bytesToInt((unsigned __int8 *)&v69) + 1LL + code);
v62 = v91++;
stack[v62] = v61;
break;
case 0x24:
v63 = pc++;
v69 = *(_BYTE *)(v63 + code);
v64 = pc++;
v70 = *(_BYTE *)(v64 + code);
v65 = pc++;
v71 = *(_BYTE *)(v65 + code);
v66 = pc++;
v72 = *(_BYTE *)(v66 + code);
*((_QWORD *)&v82 + 1) = stack[--v91];
*(_QWORD *)&v82 = stack[--v91];
if ( *((_QWORD *)&v82 + 1) == (_QWORD)v82 )
pc = bytesToInt((unsigned __int8 *)&v69);
break;
default:
continue;
}
}
}
view raw runProgram.c hosted with ❤ by GitHub
void *__fastcall initializeFunctions(char *a1)
{
int v1; // eax
int v2; // eax
int v3; // eax
int v4; // eax
int v5; // eax
int v6; // eax
int v7; // eax
int v8; // eax
int v9; // eax
int v10; // eax
void **v11; // rbx
_DWORD *v12; // rbx
__int64 v13; // rbx
__int64 v14; // rbx
void **v15; // rbx
int v16; // eax
char v18; // [rsp+1Ch] [rbp-34h]
char v19; // [rsp+1Dh] [rbp-33h]
char v20; // [rsp+1Eh] [rbp-32h]
char v21; // [rsp+1Fh] [rbp-31h]
char v22; // [rsp+20h] [rbp-30h]
char v23; // [rsp+21h] [rbp-2Fh]
char v24; // [rsp+22h] [rbp-2Eh]
char v25; // [rsp+23h] [rbp-2Dh]
int j; // [rsp+24h] [rbp-2Ch]
char i; // [rsp+2Bh] [rbp-25h]
int v28; // [rsp+2Ch] [rbp-24h]
void *ptr; // [rsp+30h] [rbp-20h]
int v30; // [rsp+38h] [rbp-18h]
int v31; // [rsp+3Ch] [rbp-14h]
v31 = 2;
v30 = 1;
ptr = malloc(0x10uLL);
v28 = 1;
for ( i = *a1; i == 17; i = a1[v16] )
{
v1 = v28++;
for ( i = a1[v1]; i != 18; i = a1[v2] )
v2 = v28++;
v3 = v28++;
v22 = a1[v3];
v4 = v28++;
v23 = a1[v4];
v5 = v28++;
v24 = a1[v5];
v6 = v28++;
v25 = a1[v6];
v7 = v28++;
v18 = a1[v7];
v8 = v28++;
v19 = a1[v8];
v9 = v28++;
v20 = a1[v9];
v10 = v28++;
v21 = a1[v10];
if ( v30 >= v31 )
{
v31 *= 2;
ptr = realloc(ptr, 8LL * v31);
}
v11 = (void **)((char *)ptr + 8 * v30 - 8);
*v11 = malloc(0x20uLL);
v12 = (_DWORD *)*((_QWORD *)ptr + v30 - 1);
*v12 = bytesToInt((unsigned __int8 *)&v18);
*(_DWORD *)(*((_QWORD *)ptr + v30 - 1) + 20LL) = 0;
v13 = *((_QWORD *)ptr + v30 - 1);
*(_DWORD *)(v13 + 16) = bytesToInt((unsigned __int8 *)&v22);
*(_DWORD *)(*((_QWORD *)ptr + v30 - 1) + 24LL) = v30 == 1;
v14 = *((_QWORD *)ptr + v30 - 1);
*(_QWORD *)(v14 + 8) = malloc(8LL * *(int *)(*((_QWORD *)ptr + v30 - 1) + 16LL));
for ( j = 0; j < *(_DWORD *)(*((_QWORD *)ptr + v30 - 1) + 16LL); ++j )
{
v15 = (void **)(*(_QWORD *)(*((_QWORD *)ptr + v30 - 1) + 8LL) + 8LL * j);
*v15 = malloc(0xA0uLL);
}
++v30;
v16 = v28++;
}
return ptr;
}
It is first Initiating the functions then Executing the code .
By analysing the initializeFunctions code, got to know the format of bytes in the .recc files
First 4 bytes
0x11 [function name] 0x12 [4 bytes of buff size] [4 bytes of function pointer]
0x11 [function name] 0x12 [4 bytes of buff size] [4 bytes of function pointer]
..
0x11 [function name] 0x12 [4 bytes of buff size] [4 bytes of function pointer]
(code bytes to be executed ...)
Python
4 Bytes buffer size for function call stack record size (num of local variables).
4 Bytes function pointer for location of the function code in code bytes.

Each function is given a index, function call is handled by that index only
Code bytes are executed by switch case in runProgram function with 26 size instruction set.

By Analysing runProgram, initFunctions -> wrote the Disassembler, Emulator to make things easier on working with recurso sricpts(leFlag.recc)
class DisAssembler:
stack = []
call_stack = []
funcs = []
current_func = 0
pc = 0
code = b''
completed = False
def __init__(self, filename):
data = open(filename, 'rb').read()
self.funcs, sep = parse_funcs(data)
self.code = data[sep:]
self.init_opcodes()
def init_opcodes(self):
self.opcodes = {
0x1 : self.subtract, # 1 Byte
0x2 : self.muiltiply, # 1 Byte
0x3 : self.add, # 1 Byte
0x8 : self.pushInt64, # 1 + 8 Bytes
0xB : self.setLocal, # 1 + 4 Bytes
0xC : self.getLocal, # 1 + 4 Bytes
0xE : self.decFunction, # 1 + 4 Bytes
0xF : self.printf, # 1 Byte
0x10 : self.stackPop, # 1 Byte
0x13 : self.divide, # 1 Byte
0x15 : self.incFunction, # 1 Byte
0x16 : self.inputd, # 1 Byte
0x17 : self.jumpNotEqual, # 1 Byte
0x18 : self.bit_or, # 1 Byte
0x19 : self.bit_and, # 1 Byte
0x1A : self.bit_xor, # 1 Byte
0x1B : self.jumpEqual, # 1 Byte
0x1C : self.jumpLessThan, # 1 Byte
0x1D : self.jumpGreaterThan,# 1 Byte
0x1E : self.jump, # 1 + 4 Bytes
0x1F : self.jumpPop, # 1 Byte
0x20 : self.incCodeByte, # 1 + 4 Bytes
0x21 : self.decCodeByte, # 1 + 4 Bytes
0x22 : self.movCodeByte, # 1 Byte
0x23 : self.pushWord, # 1 + 4 Bytes
0x24 : self.jumpIfEqual, # 1 + 4 Bytes
0xD : self.terminate, # 1 Byte
}
def emulate(self):
while not self.completed:
pc = self.pc
ins, param = self.get_instruction()
if param != None: ins(param)
else: ins()
if param == None:
print("{0:04x} -> {1:20}".format(pc, ins.__name__))
else:
print("{0:04x} -> {1:20} 0x{2:x}".format(pc, ins.__name__, param))
def disassemble(self, pc = 0):
self.pc = 0
while True:
pc = self.pc
try:
ins, param = self.get_instruction()
except Exception as e:
if self.pc < len(self.code):
print("Error occured while disassembling : ")
print(e)
if ins.__name__ == "incFunction":
param = self.funcs[param].name
for func in self.funcs:
if func.ptr == pc:
print("\n\n---------------------{}------------------------\n".format(func.name))
if param == None:
print("{0:04x} -> {1:20}".format(pc, ins.__name__))
elif isinstance(param, str):
print("{0:04x} -> {1:20} 0x{2}".format(pc, ins.__name__, param))
else:
print("{0:04x} -> {1:20} 0x{2:x}".format(pc, ins.__name__, param))
def get_instruction(self):
param_bytes = {0xB : 4, 0xC : 4, 0x15 : 4, 0x1E : 4, 0x20 : 4, 0x21 : 4, 0x23 : 4, 0x24 : 4, 0x8 : 8}
byte = self.get_bytes() ; func = self.opcodes[byte] ; param = None
if byte in param_bytes:
param = self.get_bytes(param_bytes[byte])
return func, param
def get_bytes(self, count=1):
val = self.code[self.pc:self.pc+count]
self.pc += count
return bytes_to_long(val)
def terminate(self):
self.completed = True
def subtract(self): # 0x1
p1 = self.stack.pop()
p2 = self.stack.pop()
self.stack.append(p1 - p2)
def muiltiply(self): # 0x2
p1 = self.stack.pop()
p2 = self.stack.pop()
self.stack.append(p1 * p2)
def add(self): # 0x3
p1 = self.stack.pop()
p2 = self.stack.pop()
self.stack.append(p1 + p2)
def pushInt64(self, int64): # 0x8
self.stack.append(int64)
def setLocal(self, int32): # 0xB (int 32 -> ind)
val = self.stack.pop()
self.funcs[self.current_func].setLocal(int32, val)
def getLocal(self, int32): # 0xC (int 32 -> ind)
val = self.funcs[self.current_func].getLocal(int32)
self.stack.append(val)
def decFunction(self): # 0xE
self.funcs[self.current_func].decrementFunction()
self.current_func, self.pc = self.call_stack.pop()
def printf(self): # 0xF
print(self.stack.pop())
def stackPop(self): # 0x10
self.stack.pop()
def divide(self): # 0x13
p1 = self.stack.pop()
p2 = self.stack.pop()
self.stack.append(int(p1 / p2))
def incFunction(self, int32): # 0x15 (int 32 -> func ind)
self.call_stack.append([self.current_func, self.pc])
self.current_func = int32
self.pc = self.funcs[self.current_func].ptr
self.funcs[self.current_func].incrementFunction()
def inputd(self): # 0x16
num = int(input("Enter a number (lld) : "))
self.stack.append(num)
def jumpNotEqual(self): # 0x17
p1 = self.stack.pop()
p2 = self.stack.pop()
if (p1 != p2):
self.pc = self.code.find(14, self.pc) + 1
def bit_or(self): # 0x18
p1 = self.stack.pop()
p2 = self.stack.pop()
self.stack.append(p2 | p1)
def bit_and(self): # 0x19
p1 = self.stack.pop()
p2 = self.stack.pop()
self.stack.append(p2 | p1)
def bit_xor(self): # 0x1A
p1 = self.stack.pop()
p2 = self.stack.pop()
self.stack.append(p2 ^ p1)
def jumpEqual(self): # 0x1B
p1 = self.stack.pop()
p2 = self.stack.pop()
if (p1 == p2):
self.pc = self.code.find(14, self.pc) + 1
def jumpLessThan(self): # 0x1C
p1 = self.stack.pop()
p2 = self.stack.pop()
if (p1 < p2):
self.pc = self.code.find(14, self.pc) + 1
def jumpGreaterThan(self): # 0x1D
p1 = self.stack.pop()
p2 = self.stack.pop()
if (p1 > p2):
self.pc = self.code.find(14, self.pc) + 1
def jump(self, int32): # 0x1E (int 32 -> address)
self.pc = int32
def jumpPop(self): # 0x1F
self.pc = self.stack.pop()
def incCodeByte(self, int32): # 0x20 (int 32 -> byte off)
temp = list(self.code)
temp[int32] = (temp[int32] + 1) & 256
self.code = bytes(temp)
def decCodeByte(self, int32): # 0x21 (int 32 -> byte off)
temp = list(self.code)
temp[int32] = (temp[int32] - 1) % 256
self.code = bytes(temp)
def movCodeByte(self): # 0x22
p1 = self.stack.pop()
p2 = self.stack.pop()
temp = list(self.code)
temp[p2] = temp[p1]
self.code = bytes(temp)
def pushWord(self, int32): # 0x23 (int 32 -> byte off)
b1 = self.code[int32] << 8
b2 = self.code[int32 + 1]
print("0x{:x} -> pushWord 0x{:x} | {:x}".format(self._temppc, int32, b1 + b2))
self.stack.append(b1 + b2)
def jumpIfEqual(self, int32): # 0x24 (int32 -> address)
p1 = self.stack.pop()
p2 = self.stack.pop()
if p1 == p2:
self.pc = int32
class Function:
buffer = []
buf_size = 0
def __init__(self, name, ptr, buff):
self.name = name.decode()
self.ptr = ptr
self.buf_size = buff
if (self.name == "main"):
self.incrementFunction()
def __str__(self):
return "<Function {0} -> 0x{1:04x}>".format(self.name, self.ptr)
def setLocal(self, ind, val):
self.buffer[-1][ind] = val
def getLocal(self, ind):
return self.buffer[-1][ind]
def incrementFunction(self):
self.buffer.append([0 for _ in range(self.buf_size)])
def decrementFunction(self):
self.buffer.pop()
def bytes_to_long(stream):
val = 0
for byte in stream:
val = ( val << 8 ) | byte
return val
def parse_funcs(code):
pc = 4 ; funcs = []
while code[pc] == 17:
end = code.find(18, pc + 1)
name = code[pc+1:end]
buff = bytes_to_long(code[end+1:end+5])
ptr = bytes_to_long(code[end+5:end+9])
func = Function(name, ptr, buff)
print(len(funcs), func)
funcs.append(func)
pc = end + 9
return funcs, pc
view raw disassembler.py delivered with ❤ by emgithub
More about disassembler or Chall files : https://github.com/D1r3Wolf/Recurso-Disassembler

DisAssembled leFlag.recc

By using above disassembler-> disassembled code of leFlag.recc is
---------------------main------------------------
0000 -> getLocal 0
0005 -> inputd
0006 -> setLocal 0
000b -> getLocal 0
0010 -> incFunction check
0015 -> setLocal 1
001a -> getLocal 1
001f -> printf
0020 -> terminate
---------------------antidebug------------------------
0021 -> setLocal 0
0026 -> getLocal 0
002b -> pushInt64 0
0034 -> jumpNotEqual
0035 -> pushInt64 0
003e -> decFunction
003f -> pushInt64 1
0048 -> getLocal 0
004d -> subtract
004e -> setLocal 1
0053 -> pushInt64 1
005c -> getLocal 0
0061 -> add
0062 -> setLocal 0
0067 -> pushInt64 1
0070 -> getLocal 0
0075 -> subtract
0076 -> setLocal 0
007b -> pushInt64 1
0084 -> getLocal 0
0089 -> muiltiply
008a -> setLocal 0
008f -> pushInt64 1
0098 -> getLocal 0
009d -> divide
009e -> setLocal 0
00a3 -> pushInt64 1
00ac -> getLocal 0
00b1 -> bit_and
00b2 -> setLocal 0
00b7 -> pushInt64 1
00c0 -> getLocal 0
00c5 -> bit_or
00c6 -> setLocal 0
00cb -> pushInt64 0
00d4 -> getLocal 0
00d9 -> bit_xor
00da -> setLocal 0
00df -> getLocal 1
00e4 -> incFunction antidebug
00e9 -> pushInt64 0
00f2 -> decFunction
---------------------itsATwap------------------------
00f3 -> setLocal 0
00f8 -> getLocal 0
00fd -> pushInt64 3713
0106 -> jumpNotEqual
0107 -> pushInt64 1
0110 -> decFunction
0111 -> getLocal 1
0116 -> inputd
0117 -> setLocal 1
011c -> getLocal 1
0121 -> incFunction antidebug
0126 -> getLocal 0
012b -> incFunction itsATwap
0130 -> pushInt64 0
0139 -> decFunction
---------------------oddCheck------------------------
013a -> setLocal 3
013f -> setLocal 2
0144 -> setLocal 1
0149 -> setLocal 0
014e -> getLocal 0
0153 -> pushInt64 30001
015c -> jumpNotEqual
015d -> getLocal 1
0162 -> pushInt64 26419
016b -> jumpNotEqual
016c -> getLocal 2
0171 -> pushInt64 62003745337707
017a -> jumpNotEqual
017b -> getLocal 3
0180 -> pushInt64 27955
0189 -> jumpNotEqual
018a -> pushInt64 1
0193 -> decFunction
0194 -> pushInt64 0
019d -> decFunction
---------------------evenCheck------------------------
019e -> setLocal 3
01a3 -> setLocal 2
01a8 -> setLocal 1
01ad -> setLocal 0
01b2 -> getLocal 0
01b7 -> pushInt64 25695
01c0 -> jumpEqual
01c1 -> getLocal 1
01c6 -> pushInt64 928999216
01cf -> jumpEqual
01d0 -> getLocal 2
01d5 -> pushInt64 13151
01de -> jumpEqual
01df -> getLocal 3
01e4 -> pushInt64 125
01ed -> jumpEqual
01ee -> pushInt64 0
01f7 -> decFunction
01f8 -> pushInt64 1
0201 -> decFunction
---------------------finalCheck------------------------
0202 -> getLocal 0
0207 -> getLocal 1
020c -> getLocal 2
0211 -> getLocal 3
0216 -> getLocal 4
021b -> getLocal 5
0220 -> getLocal 6
0225 -> getLocal 7
022a -> inputd
022b -> setLocal 0
0230 -> inputd
0231 -> setLocal 1
0236 -> inputd
0237 -> setLocal 2
023c -> inputd
023d -> setLocal 3
0242 -> inputd
0243 -> setLocal 4
0248 -> inputd
0249 -> setLocal 5
024e -> inputd
024f -> setLocal 6
0254 -> inputd
0255 -> setLocal 7
025a -> getLocal 0
025f -> getLocal 2
0264 -> getLocal 4
0269 -> getLocal 6
026e -> incFunction oddCheck
0273 -> setLocal 8
0278 -> getLocal 1
027d -> getLocal 3
0282 -> getLocal 5
0287 -> getLocal 7
028c -> incFunction evenCheck
0291 -> setLocal 9
0296 -> getLocal 9
029b -> getLocal 8
02a0 -> bit_and
02a1 -> setLocal 10
02a6 -> getLocal 10
02ab -> decFunction
---------------------checkThemAll------------------------
02ac -> setLocal 5
02b1 -> setLocal 4
02b6 -> setLocal 3
02bb -> setLocal 2
02c0 -> setLocal 1
02c5 -> setLocal 0
02ca -> getLocal 0
02cf -> pushInt64 18446743885531466769
02d8 -> jumpNotEqual
02d9 -> getLocal 1
02de -> pushInt64 10593957610752
02e7 -> jumpNotEqual
02e8 -> getLocal 2
02ed -> pushInt64 118730899270
02f6 -> jumpNotEqual
02f7 -> getLocal 3
02fc -> pushInt64 1346493052268
0305 -> jumpNotEqual
0306 -> getLocal 4
030b -> pushInt64 409991082872
0314 -> jumpNotEqual
0315 -> getLocal 5
031a -> pushInt64 103082098739
0323 -> jumpNotEqual
0324 -> pushInt64 1
032d -> decFunction
032e -> pushInt64 0
0337 -> decFunction
---------------------nextNextNextCheck------------------------
0338 -> getLocal 0
033d -> getLocal 1
0342 -> getLocal 2
0347 -> getLocal 3
034c -> getLocal 4
0351 -> inputd
0352 -> setLocal 0
0357 -> inputd
0358 -> setLocal 1
035d -> inputd
035e -> setLocal 2
0363 -> inputd
0364 -> setLocal 3
0369 -> inputd
036a -> setLocal 4
036f -> getLocal 1
0374 -> getLocal 0
0379 -> subtract
037a -> setLocal 5
037f -> getLocal 4
0384 -> getLocal 0
0389 -> muiltiply
038a -> setLocal 6
038f -> getLocal 2
0394 -> getLocal 5
0399 -> add
039a -> setLocal 7
039f -> getLocal 3
03a4 -> getLocal 2
03a9 -> add
03aa -> getLocal 1
03af -> add
03b0 -> getLocal 0
03b5 -> add
03b6 -> setLocal 8
03bb -> getLocal 4
03c0 -> getLocal 3
03c5 -> bit_or
03c6 -> setLocal 9
03cb -> getLocal 2
03d0 -> getLocal 3
03d5 -> subtract
03d6 -> setLocal 10
03db -> getLocal 5
03e0 -> getLocal 6
03e5 -> getLocal 7
03ea -> getLocal 8
03ef -> getLocal 9
03f4 -> getLocal 10
03f9 -> incFunction checkThemAll
03fe -> setLocal 11
0403 -> incFunction finalCheck
0408 -> setLocal 12
040d -> getLocal 12
0412 -> getLocal 11
0417 -> bit_and
0418 -> setLocal 13
041d -> getLocal 13
0422 -> decFunction
---------------------checkity------------------------
0423 -> setLocal 4
0428 -> setLocal 3
042d -> setLocal 2
0432 -> setLocal 1
0437 -> setLocal 0
043c -> getLocal 0
0441 -> pushInt64 489139534831
044a -> jumpNotEqual
044b -> getLocal 1
0450 -> pushInt64 1
0459 -> jumpNotEqual
045a -> getLocal 2
045f -> pushInt64 2
0468 -> jumpNotEqual
0469 -> getLocal 3
046e -> pushInt64 3
0477 -> jumpNotEqual
0478 -> getLocal 4
047d -> pushInt64 4
0486 -> jumpNotEqual
0487 -> pushInt64 1
0490 -> decFunction
0491 -> pushInt64 7331
049a -> incFunction antidebug
049f -> pushInt64 0
04a8 -> decFunction
---------------------midThree------------------------
04a9 -> setLocal 0
04ae -> getLocal 0
04b3 -> pushInt64 892362496
04bc -> jumpEqual
04bd -> pushInt64 0
04c6 -> decFunction
04c7 -> pushInt64 1
04d0 -> decFunction
---------------------topAndBottom------------------------
04d1 -> setLocal 0
04d6 -> getLocal 0
04db -> pushInt64 7
04e4 -> jumpNotEqual
04e5 -> pushInt64 1
04ee -> decFunction
04ef -> pushInt64 0
04f8 -> decFunction
---------------------checkTheBigs------------------------
04f9 -> setLocal 1
04fe -> setLocal 0
0503 -> getLocal 0
0508 -> getLocal 1
050d -> jumpGreaterThan
050e -> getLocal 1
0513 -> getLocal 1
0518 -> jumpEqual
0519 -> pushInt64 0
0522 -> decFunction
0523 -> getLocal 0
0528 -> getLocal 1
052d -> subtract
052e -> setLocal 2
0533 -> getLocal 2
0538 -> pushInt64 1
0541 -> pushInt64 2
054a -> pushInt64 3
0553 -> pushInt64 4
055c -> incFunction checkity
0561 -> setLocal 3
0566 -> pushInt64 4294967040
056f -> getLocal 1
0574 -> bit_and
0575 -> setLocal 4
057a -> getLocal 4
057f -> incFunction midThree
0584 -> setLocal 5
0589 -> pushInt64 255
0592 -> getLocal 0
0597 -> bit_and
0598 -> setLocal 6
059d -> pushInt64 1095216660480
05a6 -> getLocal 1
05ab -> bit_and
05ac -> setLocal 7
05b1 -> getLocal 7
05b6 -> getLocal 6
05bb -> bit_xor
05bc -> setLocal 8
05c1 -> pushInt64 1
05ca -> setLocal 9
05cf -> incFunction nextNextNextCheck
05d4 -> setLocal 10
05d9 -> getLocal 10
05de -> getLocal 9
05e3 -> bit_and
05e4 -> getLocal 5
05e9 -> bit_and
05ea -> getLocal 3
05ef -> bit_and
05f0 -> setLocal 11
05f5 -> getLocal 11
05fa -> decFunction
---------------------isItOr------------------------
05fb -> setLocal 0
0600 -> getLocal 0
0605 -> pushInt64 99
060e -> jumpNotEqual
060f -> pushInt64 1
0618 -> decFunction
0619 -> pushInt64 0
0622 -> decFunction
---------------------isItXor------------------------
0623 -> setLocal 0
0628 -> getLocal 0
062d -> pushInt64 28
0636 -> jumpEqual
0637 -> pushInt64 0
0640 -> decFunction
0641 -> pushInt64 1
064a -> decFunction
---------------------keepGoing------------------------
064b -> pushInt64 7331
0654 -> incFunction antidebug
0659 -> getLocal 0
065e -> getLocal 1
0663 -> inputd
0664 -> setLocal 0
0669 -> inputd
066a -> setLocal 1
066f -> getLocal 1
0674 -> getLocal 0
0679 -> bit_xor
067a -> setLocal 2
067f -> getLocal 2
0684 -> incFunction isItXor
0689 -> setLocal 3
068e -> getLocal 1
0693 -> getLocal 0
0698 -> bit_and
0699 -> setLocal 2
069e -> getLocal 2
06a3 -> incFunction isItOr
06a8 -> setLocal 4
06ad -> getLocal 5
06b2 -> inputd
06b3 -> setLocal 5
06b8 -> getLocal 5
06bd -> incFunction itsATwap
06c2 -> getLocal 6
06c7 -> getLocal 7
06cc -> inputd
06cd -> setLocal 6
06d2 -> inputd
06d3 -> setLocal 7
06d8 -> getLocal 6
06dd -> getLocal 7
06e2 -> incFunction checkTheBigs
06e7 -> setLocal 8
06ec -> getLocal 8
06f1 -> getLocal 4
06f6 -> bit_and
06f7 -> getLocal 3
06fc -> bit_and
06fd -> setLocal 9
0702 -> getLocal 9
0707 -> decFunction
---------------------wowzaACheck2------------------------
0708 -> setLocal 0
070d -> getLocal 0
0712 -> pushInt64 26960
071b -> jumpGreaterThan
071c -> pushInt64 1
0725 -> decFunction
0726 -> pushInt64 0
072f -> decFunction
---------------------wowzaACheck------------------------
0730 -> setLocal 0
0735 -> getLocal 0
073a -> pushInt64 269700
0743 -> jumpLessThan
0744 -> pushInt64 1
074d -> decFunction
074e -> pushInt64 0
0757 -> decFunction
---------------------yetAnotherCheck------------------------
0758 -> setLocal 0
075d -> getLocal 0
0762 -> pushInt64 28025
076b -> jumpEqual
076c -> pushInt64 0
0775 -> decFunction
0776 -> pushInt64 1
077f -> decFunction
---------------------anotherCheck------------------------
0780 -> setLocal 0
0785 -> getLocal 0
078a -> pushInt64 1057
0793 -> jumpNotEqual
0794 -> pushInt64 1
079d -> decFunction
079e -> pushInt64 0
07a7 -> decFunction
---------------------check------------------------
07a8 -> setLocal 0
07ad -> getLocal 0
07b2 -> pushInt64 102
07bb -> jumpEqual
07bc -> pushInt64 0
07c5 -> decFunction
07c6 -> getLocal 0
07cb -> inputd
07cc -> setLocal 0
07d1 -> pushInt64 1337
07da -> getLocal 0
07df -> bit_and
07e0 -> setLocal 2
07e5 -> getLocal 2
07ea -> incFunction anotherCheck
07ef -> setLocal 3
07f4 -> pushInt64 1337
07fd -> getLocal 0
0802 -> bit_or
0803 -> setLocal 4
0808 -> getLocal 4
080d -> incFunction yetAnotherCheck
0812 -> setLocal 5
0817 -> pushInt64 1337
0820 -> getLocal 0
0825 -> bit_xor
0826 -> setLocal 6
082b -> getLocal 6
0830 -> incFunction wowzaACheck
0835 -> setLocal 7
083a -> getLocal 6
083f -> incFunction wowzaACheck
0844 -> setLocal 8
0849 -> incFunction keepGoing
084e -> setLocal 9
0853 -> getLocal 9
0858 -> getLocal 8
085d -> bit_and
085e -> getLocal 7
0863 -> bit_and
0864 -> getLocal 5
0869 -> bit_and
086a -> getLocal 3
086f -> bit_and
0870 -> setLocal 10
0875 -> getLocal 10
087a -> decFunction
view raw leFlag.asm hosted with ❤ by GitHub
Note : Each Instructions work is scripted in detailed manner in disassembler.py
Based on that Reconstructed relevent python code
def int_input(string):
return int(input(string))
def midThree(a0):
return a0 == 892362496
def topAndBottom(a0):
return a0 == 7
def checkity(a0, a1, a2, a3, a4):
if a0 == 489139534831 and a1 == 1 and a2 == 2 and a3 == 3 and a4 == 4:
return 1
# antidebug(7331)
def itsATwap(a0):
if a0 == 3713: return 1
a0 = int_input("itsATwap : ")
# antidebug(a0)
itsATwap(a0)
return 0
def isItOr(a0):
return a0 == 99
def isItXor(a0):
return a0 == 28
def wowzaACheck2(a0):
return 26960 <= a0
def wowzaACheck(a0):
return 269700 >= a0
def yetAnotherCheck(a0):
return a0 == 28025
def anotherCheck(a0):
return a0 == 1057
def finalCheck():
arr = []
for i in range(8):
arr.append(int_input("i{} = ".format(i)))
t1 = oddCheck(arr[0], arr[2], arr[4], arr[6])
t2 = evenCheck(arr[1], arr[3], arr[5], arr[7])
return t1 & t2
def oddCheck(a0, a1, a2, a3):
return a0 == 30001 & a1 == 26419 & a2 == 62003745337707 & a3 == 27955
def evenCheck(a0, a1, a2, a3):
return a0 == 25695 & a1 == 928999216 & a2 == 13151 & a3 == 125
def checkThemAll(a0, a1, a2, a3, a4, a5):
return a0 == 18446743885531466769 & a1 == 10593957610752 & a2 == 118730899270 &\
a3 == 1346493052268 & a4 == 409991082872 & a5 == 103082098739
def nextNextNextCheck():
arr = []
for i in range(5):
arr.append(int_input("i{} = ".format(i)))
arr.append(arr[0] - arr[1]) # i5
arr.append(arr[0] * arr[4]) # i6
arr.append(arr[2] + arr[5]) # i7
arr.append(arr[3] + arr[2] + arr[1] + arr[0]) # i8
arr.append(arr[3] | arr[4]) # i9
arr.append(arr[3] - arr[2]) # i10
t1 = checkThemAll(arr[5], arr[6], arr[7], arr[8], arr[9], arr[10])
t2 = finalCheck()
return t1 & t2
def checkTheBigs(a0, a1):
if a1 < a0: return 0 # no neccesarily
a2 = a1 - a0
a3 = checkity(a2, 1, 2, 3, 4)
a4 = a1 & 4294967040
a5 = midThree(a4)
a6 = a0 & 255
a7 = a1 & 1095216660480
a8 = a6 ^ a7
a9 = 1
a10 =nextNextNextCheck()
return a10 & a9 & a5 & a3
def keepGoing():
# antidebug(7331)
arr = []
i0 = int_input("i0 = ")
i1 = int_input("i1 = ")
i3 = isItXor(i0 ^ i1)
i4 = isItOr(i0 & i1)
i5 = int_input("i5 = ")
itsATwap(i5)
i6 = int_input("i6 = ")
i7 = int_input("i7 = ")
i8 = checkTheBigs(i6, i7)
return i8 & i4 & i3
def check(a0):
if a0 != 102: return 0
i0 = int_input("i0 = ")
i3 = anotherCheck(i0 & 1337)
i5 = yetAnotherCheck(i0 | 1337)
i6 = 1337 ^ i0 ; i7 = wowzaACheck(i6) ; i8 = wowzaACheck2(i6)
i9 = keepGoing()
return i9 & i8 & i7 & i5 & i3
def main():
i0 = int_input("i0 = ")
print(check(i0))
view raw leFlag.py hosted with ❤ by GitHub

Reversing the logic in leFlag.recc

There is nothing but number inputs and checks in the code.
Finally removed all functions by focusing only on checks.
def int_input(string):
return int(input(string))
i0 = int_input("i0 : ")
'''
i0 == 102
'''
i1 = int_input("i1 : ") # 27745
'''
i1 & 1337 == 1057
i1 | 1337 == 28025
i1 ^ 1337 >= 269700
i1 ^ 1337 >= 26960
'''
i2 = int_input("i2 : ") # 103
i3 = int_input("i3 : ") # 123
'''
i2 ^ i3 == 28
i2 & i3 == 99
'''
i4 = int_input("i4 : ")
''' not included in flag
i4 == 3713
'''
i5 = int_input("i5 : ") # 1379099477
i6 = int_input("i6 : ") # 490518634308
'''
i6 - i5 == 489139534831
i6 & 4294967040 == 892362496
Extra checks :
every bytes in range 0x20 to 0xff
some bytes = guessed chars
'''
i7 = int_input("i7 : ") # 220707450224
i8 = int_input("i8 : ") # 408885535071
i9 = int_input("i9 : ") # 306908984117
i10 = int_input("i10 : ") # 409991082856
i11 = int_input("i11 : ") # 48
'''
i7 - i8 == 18446743885531466769
i7 * i11 == 10593957610752
i9 + i7 - i8 == 118730899270
i7 + i8 + i9 + i10 == 1346493052268
i10 | i11 == 409991082872
i10 - i9 == 103082098739
'''
# Direct nums
i12 = int_input("i12 : ") # 30001
i13 = int_input("i13 : ") # 25695
i14 = int_input("i14 : ") # 26419
i15 = int_input("i15 : ") # 928999216
i16 = int_input("i16 : ") # 62003745337707
i17 = int_input("i17 : ") # 13151
i18 = int_input("i18 : ") # 27955
i19 = int_input("i19 : ") # 125
# All adjacent number of inputs, are required numbers calculated using z3
As the last 6 number are straight forward, by some trials we got to know that long_to_bytes on those numbers will give us parts of flag

Based on the checks, written z3 script and got all the 20 input numbers.
Guessing part :( Especially for i5, i6 it became so difficult because of Too many possibilities.
My teammates helped me in this a lot, Placed all the character restrictions, guessed a last letter of i6 -> D based on i7, i8 number strings (3c0mp_3z!_).
Then seeing the possibilites noticed a string 'R3c' in i5, then it might be 'Recurso'. By going in that way got i5 -> R3cu and i6 -> r50_D

Finally by getting all the numbers
from Crypto.Util.number import long_to_bytes as l2b
inputs = [102, 27745, 103, 123, 1379099477, 490518634308, 220707450224, 408885535071, 306908984117, 409991082856, 48, 30001, 25695, 26419, 928999216, 62003745337707, 13151, 27955, 125]
flag = b''
for num in inputs:
flag += l2b(num)
print("[+] Flag is : ", flag)
view raw flag-recurso.py hosted with ❤ by GitHub

Flag : flag{R3cUr50_D3c0mp_3z!_Gu3s5_u_sh0u1d_g37_g08d_71k3_m3}




Rasm

Given the Same Recurso Execuatable with recurso compiled file rasm.recc containing a flag checking logic.
The Goal of the challenge is to get the flag checking logic in rasm.recc and steal flag

Disassembling

Tried to run the code took an input and given segmentation error. Then tried decompiling using the old script -> given error due to (Invalid Opcode)
It seems like there is an error in Code bytes, just to know all the instructions, skipped all the errors. Then found interesting at the end of the disassembly

---------------------main------------------------
0000 -> pushInt64 319
0009 -> inputd
000a -> pushInt64 265
0013 -> add
0014 -> jumpPop
0015 -> jumpPop
0016 -> subtract
0018 -> subtract
.
.
.
0236 -> decFunction
0237 -> pushInt64 0x200 0100 0015
0240 -> pushInt64 0x238
0249 -> setLocal 0x0
024e -> getLocal 0x0
0253 -> pushWord 0x23e
0258 -> movCodeByte
0259 -> decCodeByte 0x238
025e -> pushWord 0x23e
0263 -> getLocal 0x0
0268 -> movCodeByte
0269 -> incCodeByte 0x23f
026e -> pushInt64 0x0
0277 -> pushWord 0x23e
027c -> jumpIfEqual 0x286
0281 -> jump 0x24e
0286 -> getLocal 0x0
028b -> pushWord 0x23c
0290 -> movCodeByte
0291 -> decCodeByte 0x238
0296 -> pushWord 0x23c
029b -> getLocal 0x0
02a0 -> movCodeByte
02a1 -> incCodeByte 0x23d
02a6 -> pushInt64 0x100
02af -> pushWord 0x23c
02b4 -> jumpIfEqual 0x2be
02b9 -> jump 0x286
02be -> getLocal 0x0
02c3 -> pushWord 0x23a
02c8 -> movCodeByte
02c9 -> decCodeByte 0x238
02ce -> pushWord 0x23a
02d3 -> getLocal 0x0
02d8 -> movCodeByte
02d9 -> incCodeByte 0x23b
02de -> pushInt64 0x238
02e7 -> pushWord 0x23a
02ec -> jumpIfEqual 0x2f6
02f1 -> jump 0x2be
02f6 -> pushInt64 0x2
02ff -> printf
0300 -> jump 0x9
Note : I have skipped much of assembly which looks uneven from (0x19 to 0x235). for complete dissambly : rasm-full.asm
Code from 0x0237 to 0x0300 makes sense now, The assembly code is encoded some how, the code below is to decode that above code bytes and jump there.
There are some instructions in Recurso lang which can modify the execution code bytes at run time. So Rasm is having self modifying logic.

Decoding the Bytecode

The code below (0x237 to 0x300) is decoding the above code (0x015 to 0x237) by decrementing every byte by 1.
And found a code at above (0x0 to 0x14) which is causing Segmentation fault .
0000 ->  pushInt64             0x13f
0009 ->  inputd              
000a ->  pushInt64             0x109
0013 ->  add                 
0014 ->  jumpPop
JavaScript
The code above is to , take an input and jump to (265 + input_val)
Jump address should be 0x237. So input = 0x237 - 265 = 302 (in decimal)
$ ./Recurso rasm.recc 
302
2

JavaScript
If we give the input 302, segmentation fault won't come, It decodes above code and prints(2) and jump to above. Asks again a input address to jump.

Analyzing Decoded Bytecode

Decoded the rasm.recc into drasm.recc by a script and then disassembled.

---------------------main------------------------
0000 -> pushInt64 0x13f
0009 -> inputd
000a -> pushInt64 0x109
0013 -> add
0014 -> jumpPop
0015 -> jump 0x25
001a -> inputd
001b -> setLocal 0x0
0020 -> jump 0x33
0025 -> pushInt64 0x3e8
002e -> setLocal 0x0
0033 -> pushWord 0x7
0038 -> pushWord 0x7
003d -> pushInt64 0x7
0046 -> add
0047 -> movCodeByte
0048 -> incCodeByte 0x8
004d -> pushWord 0x7
0052 -> pushInt64 0x6
005b -> add
005c -> pushWord 0x7
0061 -> movCodeByte
0062 -> pushWord 0x7
0067 -> pushWord 0x7
006c -> pushInt64 0x5
0075 -> add
0076 -> movCodeByte
0077 -> incCodeByte 0x8
007c -> pushWord 0x7
0081 -> pushInt64 0x4
008a -> add
008b -> pushWord 0x7
0090 -> movCodeByte
0091 -> pushWord 0x7
0096 -> pushWord 0x7
009b -> pushInt64 0x3
00a4 -> add
00a5 -> movCodeByte
00a6 -> incCodeByte 0x8
00ab -> pushWord 0x7
00b0 -> pushInt64 0x2
00b9 -> add
00ba -> pushWord 0x7
00bf -> movCodeByte
00c0 -> pushWord 0x7
00c5 -> incCodeByte 0x8
00ca -> pushWord 0x7
00cf -> movCodeByte
00d0 -> pushWord 0x7
00d5 -> pushInt64 0x3
00de -> movCodeByte
00df -> incCodeByte 0x8
00e4 -> incCodeByte 0x8
00e9 -> incCodeByte 0x8
00ee -> incCodeByte 0x8
00f3 -> incCodeByte 0x8
00f8 -> incCodeByte 0x8
00fd -> incCodeByte 0x8
0102 -> incCodeByte 0x8
0107 -> incCodeByte 0x8
010c -> incCodeByte 0x8
0111 -> incCodeByte 0x8
0116 -> incCodeByte 0x8
011b -> incCodeByte 0x8
0120 -> incCodeByte 0x8
0125 -> incCodeByte 0x6
012a -> pushWord 0x5
012f -> getLocal 0x0
0134 -> jumpIfEqual 0x13e
0139 -> jump 0x33
013e -> pushInt64 0x617b7b67616c66
0147 -> pushInt64 0x4142434445464748
0150 -> pushInt64 0x735773215f6d35
0159 -> pushInt64 0x494a4b4c4d4e4f50
0162 -> pushInt64 0x5f6b7733635f30
016b -> pushInt64 0x5152535455565758
0174 -> pushInt64 0x6e376b5f545562
017d -> pushInt64 0x595a303132333435
0186 -> pushInt64 0x723163355f3462
018f -> pushInt64 0x363738397b7d5b5d
0198 -> pushInt64 0x6d5b3768775f79
01a1 -> pushInt64 0x2d3d5f2b21402324
01aa -> pushInt64 0x5f34215f745355
01b3 -> pushInt64 0x255e262a28292f3f
01bc -> pushInt64 0x5f377961776c34
01c5 -> pushInt64 0x2c2e3c3e3b3a2722
01ce -> pushInt64 0x2132476e346863
01d7 -> pushInt64 0x0
01e0 -> pushInt64 0x7d2131
01e9 -> pushWord 0x2
01ee -> inputd
01ef -> bit_xor
01f0 -> incCodeByte 0x2
01f5 -> jumpIfEqual 0x1ff
01fa -> jump 0x22c
01ff -> stackPop
0200 -> decCodeByte 0x6
0205 -> pushWord 0x5
020a -> pushInt64 0x0
0213 -> jumpIfEqual 0x21d
0218 -> jump 0x1e9
021d -> pushInt64 0x1
0226 -> printf
0227 -> jump 0x236
022c -> pushInt64 0x0
0235 -> printf
0236 -> terminate
0237 -> pushInt64 0x20001000015
0240 -> pushInt64 0x238
0249 -> setLocal 0x0
024e -> getLocal 0x0
0253 -> pushWord 0x23e
0258 -> movCodeByte
0259 -> decCodeByte 0x238
025e -> pushWord 0x23e
0263 -> getLocal 0x0
0268 -> movCodeByte
0269 -> incCodeByte 0x23f
026e -> pushInt64 0x0
0277 -> pushWord 0x23e
027c -> jumpIfEqual 0x286
0281 -> jump 0x24e
0286 -> getLocal 0x0
028b -> pushWord 0x23c
0290 -> movCodeByte
0291 -> decCodeByte 0x238
0296 -> pushWord 0x23c
029b -> getLocal 0x0
02a0 -> movCodeByte
02a1 -> incCodeByte 0x23d
02a6 -> pushInt64 0x100
02af -> pushWord 0x23c
02b4 -> jumpIfEqual 0x2be
02b9 -> jump 0x286
02be -> getLocal 0x0
02c3 -> pushWord 0x23a
02c8 -> movCodeByte
02c9 -> decCodeByte 0x238
02ce -> pushWord 0x23a
02d3 -> getLocal 0x0
02d8 -> movCodeByte
02d9 -> incCodeByte 0x23b
02de -> pushInt64 0x238
02e7 -> pushWord 0x23a
02ec -> jumpIfEqual 0x2f6
02f1 -> jump 0x2be
02f6 -> pushInt64 0x2
02ff -> printf
0300 -> jump 0x9
Things i have noticed in the flag checking logic.
  • 0x013e to 0x01e0 : Pushing a 8 byte numbers (Alternatively a flag number and a dummy number) to the stack
  • On each flag number : by doing long_to_bytes -> 7 bytes of flag is coming, in reverse order and one char is wrong.
  • 0x0033 to 0x0139 : A loop, in each iteration, it can update one flag num and move to other flag num address.
  • On each flag number : reverse one number (flag 7 bytes , in same space of 8 bytes) which leads to 5th byte null (0).
  • loop variables, other variables are maintained on code bytes only, they are accessed and modified using the instructions
  • No of times the above loop will run depends on local var 0. we can set that varible by input, at address 0x001a
  • So second input (used for second jump) is input + 265 = 0x001a. So input = 0x1a - 265 = -239
  • Third input (no of iterations of the loop) has to be 10. As there are 10 such flag numbers
  • 0x01e9 to 0x0236 : A loop to take numbers (which are combined together can form flag) and check them and return 1
  • On each iteration (i) : It checks input ^ i*0x100 == stackTop(flag number), and pops dummy number once.

Taking those 10 flag numbers in disassembly from 0x013e alternatively in the instructions.
from Crypto.Util.number import long_to_bytes as l2b
from Crypto.Util.number import bytes_to_long as b2l
flag_nums = [0x617b7b67616c66, 0x735773215f6d35, 0x5f6b7733635f30, 0x6e376b5f545562, 0x723163355f3462, 0x6d5b3768775f79, 0x5f34215f745355, 0x5f377961776c34, 0x2132476e346863, 0x7d2131]
flag = b''
for i in range(10):
num = b2l( l2b(flag_nums[i])[::-1] )
num ^= (9-i)*0x100
flag += l2b(num)
print("[+] Flag is : ", flag.decode())
view raw flag-rasm.py hosted with ❤ by GitHub

Flag : flag{ra5m_!s_s0_c3wl_bUT_k1nb4_5c4ry_wh7_mUSt_!7_4lway5_ch4nG3!1!}




Thanks for reading !...

Comments

Popular posts from this blog

Square CTF 2019 Writeup's

Confidence CTF 2020 `Cat web` challenge writeup