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}