Automating Ghidra - part 3
In the third installment of this series (if you haven't read/seen here's - part 1 & part 2) we will be reconstructing program flow. This is useful for tasks such as Towel's armageddon.
Note: If you prefer watching, have a look at my YouTube channel.
The difficulty of this crackme is that it contains (as noted in the description) slight obfuscation. It has a form of program flow obfuscation that looks like the following:
LAB_00014a0c XREF[1] 00014a04 (j)
014a0c 00 48 stmdb sp!,{r11 lr}
2d e9
014a10 00 00 b LAB_00014a18
00 ea
014a14 07 ?? 07h
014a15 43 ?? 43h C
014a16 94 ?? 94h
014a17 2b ?? 2Bh +
LAB_00014a18 XREF[1] 00014a10 (j)
014a18 04 b0 add r11,sp,#0x4
8d e2
014a1c 00 00 b LAB_00014a24
00 ea
014a20 d9 ?? D9h
014a21 5a ?? 5Ah Z
014a22 05 ?? 05h
014a23 ec ?? ECh
LAB_00014a24 XREF[1] 00014a1c (j)
014a24 58 d0 sub sp,sp,#0x58
4d e2
014a28 00 00 b LAB_00014a30
00 ea
014a2c 1b ?? 1Bh
014a2d f5 ?? F5h
014a2e 8e ?? 8Eh
014a2f f4 ?? F4h
LAB_00014a30 XREF[1] 00014a28 (j)
014a30 f8 08 ldr r0=>s_---------------_-=UMDCTF_2019=-_-_0 = 000153d4
9f e5 = "--------
014a34 00 00 b LAB_00014a3c
00 ea
014a38 7e ?? 7Eh ~
014a39 05 ?? 05h
014a3a 8a ?? 8Ah
014a3b 5f ?? 5Fh _
LAB_00014a3c XREF[1] 00014a34 (j)
014a3c 50 ee bl puts int puts(ch
ff eb
014a40 00 00 b LAB_00014a48
00 ea
014a44 a2 ?? A2h
014a45 d5 ?? D5h
014a46 66 ?? 66h f
014a47 3a ?? 3Ah :
The program listing is a mess. Only one valid instruction followed by a jump and few bytes of garbage makes the analysis a bit tougher. Disassembly is looking correct, but if we would like to check the disassembly we have a problem.
Here comes Ghidra's scripting API. We could use the API to extract instructions, skip those unconditional jumps and reconstruct the code.
To do that we can use currentProgram.getListing().getInstructionAt(addr)
to check if the instruction is an unconditional jump, we can get the Flow type information
t = instruction.getFlowType()
if t == RefType.UNCONDITIONAL_JUMP:
There's one more tricky part - in order to get an instruction string we need to do the following
codeUnitFormat = CodeUnitFormat(CodeUnitFormatOptions(CodeUnitFormatOptions.ShowBlockName.ALWAYS,CodeUnitFormatOptions.ShowNamespace.ALWAYS,"",True,True,True,True,True,True,True))
codeUnitFormat.getRepresentationString(instruction)
Without that we would be missing some important stuff like references, and string.
The full script.
Running it will give us listing without those jumps
000104f4: b .text:check_flag_1
000104fc: stmdb sp!,{r11 lr}
00010508: add r11,sp,#0x4
00010514: sub sp,sp,#0x8
00010520: str flag,[r11,#local_c+0x4]
0001052c: ldr r3,[r11,#local_c+0x4]
00010538: add r3,r3,#0x15
00010544: ldrb r3,[r3,#0x0]
00010550: cpy r2,r3
0001055c: ldr r3,[r11,#local_c+0x4]
00010568: add r3,r3,#0x27
00010574: ldrb r3,[r3,#0x0]
00010580: mul r3,r3,r2
0001058c: ldr r2,[r11,#local_c+0x4]
00010598: add r2,r2,#0x1
000105a4: ldrb r2,[r2,#0x0]
000105b0: mul r3,r2,r3
000105bc: ldr r2,[r11,#local_c+0x4]
000105c8: add r2,r2,#0x11
000105d4: ldrb r2,[r2,#0x0]
000105e0: add r2,r3,r2
000105ec: ldr r3,[r11,#local_c+0x4]
000105f8: add r3,r3,#0x1e
00010604: ldrb r3,[r3,#0x0]
00010610: cpy r1,r3
0001061c: ldr r3,[r11,#local_c+0x4]
00010628: add r3,r3,#0x13
00010634: ldrb r3,[r3,#0x0]
00010640: mul r3,r3,r1
0001064c: add r3,r2,r3
00010658: ldr r2,[.text:DAT_000106d0]
00010664: cmp r3,r2
00010670: beq .text:LAB_000106a4
0001067c: ldr flag=>.rodata:s__[!]_Code_did_not_validate!_:(_000153b4,[.text:PTR_s__[!]_Code_did_not_validate!_:(_000106d4]
00010688: bl .plt:puts
....
Now we can analyze it without problems.