Reverse Engineeringhard
Hard x64 Linux
by Pavel Knespl (capyplivl)
A 690 KB static, stripped x86-64 ELF password checker littered with bogus call instructions (opaque-predicate junk). The check is a Blowfish-CTR stream cipher, so the flag is the embedded blob XORed with the keystream from emulating the cipher on zeros.
Solve
- Read
main(0x4016d0):if (argc==2 && check(argv[1])==1) puts("You beat the x64 Linux Hard binary!");. Throughout the code arexor eax,eax; je .next; call <junk>; .next:sequences. Thejeis always taken (ZF set byxor), so thecallnever runs; it only desyncs linear disassembly. Trace through the jump and ignore each call. - Reverse
check(fcn.00401e80->fcn.004018d0). It builds a 52-byte expected blob from static constants (no literals), runstransform(input, 52, key=0x123456789abcdef0)in place, then compares 52 bytes. - Identify
transform(0x4018d0) as textbook Blowfish: 18-entry P-array, four 256-entry S-boxes (generated at0x4a7b00), round functionF=((S0[a]+S1[b])^S2[c])+S3[d], 16 rounds. The tail encrypts the block counter andxor byte [rdx], clinto the data, so it is CTR/OFB mode. Keystream depends only on key + position, givingflag = blob XOR transform(zeros). - Emulate
transformwith Unicorn. Map the four PT_LOAD segments and pointrdiat a 52-byte buffer. Zero the CPU-dispatch flag at0x4a60d0(file holds1, which selects a scalar fallback) to force the SSE path:
mu.mem_write(0x4a60d0, b'\x00\x00\x00\x00')
keystream = run_transform(b'\x00'*52)
assert xor(run_transform(bytes(range(52))), bytes(range(52))) == keystream
- Get the blob by emulating
checkwith allcalls stubbed (rax=0) and reading the compare buffer at the setup point0x402006. Blob bytes are staticmovs, so stubbing libc/anti-debug calls leaves them intact. XOR blob with keystream to get the flag. - Confirm on the real binary:
$ ./Hard_x64_Linux 'SVIUSCG{It_L00ks_l1k3_U_Know_H0w_t0_P4tch_Binaries!}'
You beat the x64 Linux Hard binary!
Flag: SVIUSCG{It_L00ks_l1k3_U_Know_H0w_t0_P4tch_Binaries!}