Software Reverse Engineering: Diffusing Phase 3

Abhinav Thakur
4 min readApr 11, 2022

--

Baby, I hate little secrets you keep from me x_x

The article continues the previous one where we diffused phase 2 of bomb binary. Let’s get started with phase 3 on Windows platform using Windbg.

This writeup is a part of the article series —

Rescue Mission: Phase_3

Looking at the disassembly for phase_3() (uf bomb!phase_3).

uf bomb!phase_3

I’ve ommited some assembly instructions at the function prologue to hide unnecessary complexity. Anyways, we first need to figure out what kind of input does this function expect. Skimming through it, we find the first function call to sscanf() whose function prototype is —

int sscanf(const char *str, const char *format, ...);

If we somehow figure out the format string parameter for sscanfI(), we’ll know what input it is expecting from str (drawing stack diagram will confirm it right away that [rbp+160h] here refers to shadow store inside main stack frame where the user input string in stored by 1st instruction of phase_3()). As we can see just before the call to sscanf(), its parameters are filled in RCX, RDX, R8 and R9 in the same order (remember Microsoft’s x64 calling conventions discussed). Since we want to figure out format string parameter (*format), we need examine the string stored inside RDX register before the call to sscanf() as shown below —

0:001> uf bomb!phase_3
...
72 00007ff6`037721ed lea rdx,[bomb!`string' (00007ff6`0377c220)]
72 00007ff6`037721f4 mov rcx,qword ptr [rbp+160h]
...
0:001> da 00007ff6`0377c220
00007ff6`0377c220 "%d %d"

Since this function expects us to pass 2 integers (as per the format string “%d %d” we saw). Let’s give this input and reach the call site of sscanf().

phase_3 input as per “%d %d”
0:001> .restart
0:001> .reload /f
0:001> bp bomb!phase_3
0:001> g
0:001> pa 00007ff6`037721fb
0:000> da rcx
00007ff6`037802a0 "1 3"

Therefore, the call to sscanf() becomes —

sscanf (user_input_string, "%d %d", rpb+0x4, rbp+0x24);

Validating user input

After sscanf(), the program checks if it returned 2 (confirming sscanf read 2 integers) and moves forward with validating user input.

first integer validation

Interpreting above instructions, the bomb explodes if the first integer we give is greater than 7. This is followed by some unnecessary instructions which can be skipped over.

NOTE: The ja instruction suggests that it is to perform an unsigned comparison in contrast to jg which would have indicated a signed comparison.

irrelevant to diffusing phase_3
second integer validation

According to above disassembly, again the bomb explodes if first integer entered by user is greater than 5. The instruction cmp dword ptr [rbp+44h],eax then compares the second integer (inside eax) with value at [rbp+44h] . Let’s display a DWORD (dd) value at [rbp+44h].

0:000> dd rbp+44 L1
00000062`aacff3f4 ffffffe6

Since, 0xffffffe6 is -26 in signed decimal (i.e. 2’s complement of 0xffffffe6 ). Therefore, the input becomes (num < 5) -26

phase_3 solved

Pseudocode for phase_3() disassembly is as follows —

Pseudocode for phase_3

Epilogue

While diffusing phase 3, we explored simple if-else code construct which involved dealing with signed/unsigned comparisons. Let’s continue right into the writeup for diffusing phase_4.

Cheers,
Abhinav Thakur
(a.k.a
compilepeace)

Connect on Linkedin

--

--