What does
func("b4by_r3vers1ng_ch4ll")
return?
func:
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [-16+rbp], rdi
mov rax, 0x0cafebabe
mov QWORD PTR [-8+rbp], rax
..B1.2:
mov rax, QWORD PTR [-16+rbp]
movsx eax, BYTE PTR [rax]
movsx rax, al
test eax, eax
je ..B1.4
mov rax, 0x0deadc0de
imul rax, QWORD PTR [-8+rbp]
mov QWORD PTR [-8+rbp], rax
mov rax, QWORD PTR [-16+rbp]
movsx eax, BYTE PTR [rax]
movsx rax, al
xor rax, QWORD PTR [-8+rbp]
mov QWORD PTR [-8+rbp], rax
mov eax, 1
add rax, QWORD PTR [-16+rbp]
mov QWORD PTR [-16+rbp], rax
jmp ..B1.2
..B1.4:
mov rax, QWORD PTR [-8+rbp]
leave
ret
This is some basic x86 assembly, but it looks like it's got some annoying things like multiplication with integer overflows and xorring, so we'll just write a C program to emulate it. Remember, rdi is the argument (which will be the address of the string), and rax is the return value.
#include <stdio.h>
#include <sys/types.h>
typedef int8_t BYTE;
typedef int16_t WORD;
typedef int32_t DWORD;
typedef int64_t QWORD;
char inp[] = "b4by_r3vers1ng_ch4ll";
int main() {
QWORD rbp16 = (QWORD)inp;
QWORD rbp8 = 0xcafebabe;
QWORD rax;
while (*(char *)rbp16) {
rax = 0xdeadc0de;
rax *= rbp8;
rbp8 = rax;
rax = *(char *)rbp16;
rax ^= rbp8;
rbp16++;
rax = rbp16;
}
printf("%ld", rax);
}
This prints out 6295620
, giving us the flag of IJCTF{6295620}
.
What does
func()
return?
l0rd_v0ld3m0rt:
addiu $sp,$sp,-24
sw $fp,20($sp)
move $fp,$sp
sw $4,24($fp)
lw $2,24($fp)
nop
sltu $2,$2,2
bne $2,$0,$L2
nop
lw $2,24($fp)
nop
sltu $2,$2,3
bne $2,$0,$L3
nop
lw $2,24($fp)
nop
andi $2,$2,0x1
bne $2,$0,$L3
nop
$L2:
move $2,$0
b $L4
nop
$L3:
lw $3,24($fp)
li $2,2 # 0x2
bne $3,$2,$L5
nop
li $2,1 # 0x1
b $L4
nop
$L5:
li $2,3 # 0x3
sw $2,8($fp)
b $L6
nop
$L8:
lw $2,8($fp)
lw $3,24($fp)
nop
bne $2,$0,1f
divu $0,$3,$2
break 7
1:
mfhi $2
bne $2,$0,$L7
nop
move $2,$0
b $L4
nop
$L7:
lw $2,8($fp)
nop
addiu $2,$2,2
sw $2,8($fp)
$L6:
lw $3,8($fp)
lw $2,8($fp)
nop
mult $3,$2
mflo $3
lw $2,24($fp)
nop
sltu $2,$2,$3
beq $2,$0,$L8
nop
li $2,1 # 0x1
$L4:
move $sp,$fp
lw $fp,20($sp)
addiu $sp,$sp,24
j $31
nop
func:
addiu $sp,$sp,-40
sw $31,36($sp)
sw $fp,32($sp)
move $fp,$sp
sw $0,24($fp)
sw $0,28($fp)
b $L10
nop
$L11:
lw $4,28($fp)
jal l0rd_v0ld3m0rt
nop
move $3,$2
lw $2,24($fp)
nop
addu $2,$2,$3
sw $2,24($fp)
lw $2,28($fp)
nop
addiu $2,$2,1
sw $2,28($fp)
$L10:
lw $3,28($fp)
li $2,195887104 # 0xbad0000
ori $2,$2,0xf00d
sltu $2,$3,$2
bne $2,$0,$L11
nop
lw $2,24($fp)
move $sp,$fp
lw $31,36($sp)
lw $fp,32($sp)
addiu $sp,$sp,40
j $31
nop
This is some mips assembly, and you can see inside func that 28($fp)
contains a loop index and it will keep looping until
it reaches 0xbadf00d
, which is a really long loop. You can also see 24($fp)
contains the sum of all the l0rd_v0ld3m0rt
calls,
which is being called with the loop index as an argument ($4
contains the argument and $2
contains the return value). In
l0rd_v0ld3m0rt
, we can see that L4
just returns whatever's in $2
, and L2
returns 0. If our argument is less than 2, it
jumps to L2
and returns 0. We can also see that L3
is where the program logic starts. If our argument, which is in 24($fp)
(note that this is a different $fp
than func
), is less than 3 or is odd, we jump to L3
, otherwise we jump to L2
and
return 0. And basically there's a loop with an index stored in 8($fp)
that stops when 8($fp)
squared is greater than the
argument. And, in each body of the loop, it takes the argument mod the index, and if it's 0 it returns 0. If it gets through the
whole loop, it returns 1. So this whole thing was actually a prime checker the whole time. The goal is to find the number of primes
under 0xbadf00d
, which I'm sure you can figure out how to do with some googling.
What does
prince_vegeta("sup3r_saiy4n$_4r3_c00l")
return?
__SP_L__ = 0x3d
__tmp_reg__ = 0
prince_vegeta:
push r28
push r29
rcall .
rcall .
in r28,__SP_L__
in r29,__SP_H__
std Y+4,r25
std Y+3,r24
ldd r24,Y+3
ldd r25,Y+4
std Y+2,r25
std Y+1,r24
rjmp .L2
.L3:
ldd r24,Y+1
ldd r25,Y+2
adiw r24,1
std Y+2,r25
std Y+1,r24
.L2:
ldd r24,Y+1
ldd r25,Y+2
movw r30,r24
ld r24,Z
tst r24
brne .L3
ldd r18,Y+1
ldd r19,Y+2
ldd r24,Y+3
ldd r25,Y+4
movw r20,r18
sub r20,r24
sbc r21,r25
movw r24,r20
pop __tmp_reg__
pop __tmp_reg__
pop __tmp_reg__
pop __tmp_reg__
pop r29
pop r28
ret
This is AVR assembly, and it's important to note that the registers are all 8 bits, and there are 16 bit pointers Y and Z, where Y is R29:R28 and Z is R31:R30. Basically, what this program does is it stores a pointer to the start of the string, then keeps incrementing another pointer until it finds the nullbyte at the end of the string, then it subtracts those two pointers and returns the difference. So, it just finds the length of the string, which is 22.