Broken Packer
rev150
Description: Looks like this packer can not unpack what has been packed :( There are 2 mistakes in unpacking procedure. It leads to the error. Try to fix unpacker and figure out what is inside.
Webpage: broken_packer.zip
We are given an archive with 2 files - a packer and a packed executable. As it later turns out the packer is not even needed to solve it, so let’s concentrate on the packed application, packed.
Running the application from command line yields a segfault:
1
2
$ ./packed
Segmentation fault
We better fix those errors the description is talking about. Let’s open it in IDA:

The unpacking routine is very simple and the errors are easy to spot. The loop index is in rax so xor [rdx], dl should really be xor [rax], dl. Also, we go byte-by-byte, so the index increment should be add rax, 1, not add rax,2. Let’s patch the binary to correct these.
Patching the application gets us further:
1
2
3
$ ./packed
The hardest part is overcome.
Segmentation fault
Let’s debug the application and look at the disassembly to see what function outputs that:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
.text:0000000000400BA9 sub_400BA9 proc near ; DATA XREF: sub_400990+1D
.text:0000000000400BA9
.text:0000000000400BA9 var_10 = qword ptr -10h
.text:0000000000400BA9 var_4 = dword ptr -4
.text:0000000000400BA9
.text:0000000000400BA9 push rbp
.text:0000000000400BAA mov rbp, rsp
.text:0000000000400BAD sub rsp, 10h
.text:0000000000400BB1 mov [rbp+var_4], edi
.text:0000000000400BB4 mov [rbp+var_10], rsi
.text:0000000000400BB8 lea rdi, aTheHardestPart ; "The hardest part is overcome."
.text:0000000000400BBF call sub_407FC0
.text:0000000000400BC4 mov rax, [rbp+var_10]
.text:0000000000400BC8 add rax, 8
.text:0000000000400BCC mov rax, [rax]
.text:0000000000400BCF lea rsi, aCryp ; "cryp"
.text:0000000000400BD6 mov rdi, rax
.text:0000000000400BD9 call sub_400370
.text:0000000000400BDE test eax, eax
.text:0000000000400BE0 jnz short loc_400BF5
.text:0000000000400BE2 mov rax, [rbp+var_10]
.text:0000000000400BE6 add rax, 10h
.text:0000000000400BEA mov rax, [rax]
.text:0000000000400BED mov rdi, rax
.text:0000000000400BF0 call sub_400AAE
.text:0000000000400BF5
.text:0000000000400BF5 loc_400BF5: ; CODE XREF: sub_400BA9+37
.text:0000000000400BF5 mov rax, [rbp+var_10]
.text:0000000000400BF9 add rax, 8
.text:0000000000400BFD mov rax, [rax]
.text:0000000000400C00 lea rsi, aDecr ; "decr"
.text:0000000000400C07 mov rdi, rax
.text:0000000000400C0A call sub_400370
.text:0000000000400C0F test eax, eax
.text:0000000000400C11 jnz short loc_400C18
.text:0000000000400C13 call sub_400B39
.text:0000000000400C18
.text:0000000000400C18 loc_400C18: ; CODE XREF: sub_400BA9+68
.text:0000000000400C18 mov eax, 0
.text:0000000000400C1D leave
.text:0000000000400C1E retn
.text:0000000000400C1E sub_400BA9 endp
Looks like there is a cryp and a decr command line parameters. Assuming we need to decrypt the flag, we first need to find where the encrypted flag is. Running with cryp parameter gives us a hint at what it looks like:
1
2
3
$ ./packed cryp h4ck1t{test}
The hardest part is overcome.
0x95, 0xc0, 0x90, 0x8e, 0xc5, 0x8f, 0x74, 0x31, 0x6b, 0x63, 0x34, 0x68,
The encrypted bytes look like this:
1
95 C0 90 8E C5 8F 74 31 6B 63 34 68 ......t1kc4h
So the ending is just the flag prefix, reversed. Luckily, the encrypted flag is embedded in the binary:

Now let’s analyze the decryption algorithm so that we could reproduce it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
.text:0000000000400B39 sub_400B39 proc near ; CODE XREF: sub_400BA9+6A
.text:0000000000400B39
.text:0000000000400B39 var_9 = byte ptr -9
.text:0000000000400B39 var_8 = dword ptr -8
.text:0000000000400B39 var_4 = dword ptr -4
.text:0000000000400B39
.text:0000000000400B39 push rbp
.text:0000000000400B3A mov rbp, rsp
.text:0000000000400B3D sub rsp, 10h
.text:0000000000400B41 lea rdi, byte_6B20A0
.text:0000000000400B48 call sub_417BB0
.text:0000000000400B4D mov [rbp+var_8], eax
.text:0000000000400B50 mov eax, [rbp+var_8]
.text:0000000000400B53 mov [rbp+var_4], eax
.text:0000000000400B56
.text:0000000000400B56 loc_400B56: ; CODE XREF: sub_400B39+6B
.text:0000000000400B56 cmp [rbp+var_4], 0
.text:0000000000400B5A js short loc_400BA6
.text:0000000000400B5C mov eax, [rbp+var_4]
.text:0000000000400B5F movsxd rdx, eax
.text:0000000000400B62 lea rax, byte_6B20A0
.text:0000000000400B69 movzx ecx, byte ptr [rdx+rax]
.text:0000000000400B6D mov eax, [rbp+var_8]
.text:0000000000400B70 sub eax, 1
.text:0000000000400B73 sub eax, [rbp+var_4]
.text:0000000000400B76 movsxd rdx, eax
.text:0000000000400B79 lea rax, byte_6B20A0
.text:0000000000400B80 movzx eax, byte ptr [rdx+rax]
.text:0000000000400B84 xor eax, ecx
.text:0000000000400B86 xor eax, 0FFFFFF80h
.text:0000000000400B89 mov [rbp+var_9], al
.text:0000000000400B8C mov eax, [rbp+var_4]
.text:0000000000400B8F movsxd rdx, eax
.text:0000000000400B92 lea rax, byte_6B20A0
.text:0000000000400B99 movzx ecx, [rbp+var_9]
.text:0000000000400B9D mov [rdx+rax], cl
.text:0000000000400BA0 sub [rbp+var_4], 1
.text:0000000000400BA4 jmp short loc_400B56
.text:0000000000400BA6 ; ---------------------------------------------------------------------------
.text:0000000000400BA6
.text:0000000000400BA6 loc_400BA6: ; CODE XREF: sub_400B39+21
.text:0000000000400BA6 nop
.text:0000000000400BA7 leave
.text:0000000000400BA8 retn
.text:0000000000400BA8 sub_400B39 endp
Essentially the algorithm reverses the flag, and then encodes the first part of it by XORing it with characters in the second part and with 0x80.
To decode it let’s write a short script:
1
2
3
4
5
6
7
8
9
data = bytearray(open("packed","rb").read())
off = 0xb20a0
out = ""
for x in range(0x30/2):
out += chr(data[off + x] ^ data[off + 0x30 - x - 1] ^ 0x80)
print (out + str(data[off + 0x30/2: off + 0x30]))[::-1]
The flag is h4ck1t{mor0ns_rel1es_on_p4ck3r5_vari0rs_d03sn0t}:
1
2
$ python solve.py
h4ck1t{mor0ns_rel1es_on_p4ck3r5_vari0rs_d03sn0t}