# Will Reverse Engineer for Food: with GDB peda and IDA – Part I

I was searching for new work some 5 years ago, and one of an excellent company sent me a Reverse Engineering challenge for a Linux Binary. This post gives details of how I solved it.

Note: Ghidra was not available at the time and I wanted to do it raw using GDB and dynamic analysis just for fun by looking at assembly. Although I used IDA for understanding program logic better then work in GDB to conduct breakpoint analysis.

There are plenty of binaries out there for using techniques given here – I don’t want to share the one that this particular company sent me.

\*\*Basic Analysis: \*\*

* Binary is completely stripped of any symbols or any debugging information – No Information other than strings inside the code is available
    
* Binary has also been compiled with protection mechanism like Canaries, NX(Non-Executable Stack), PIE and RELRO is fully turned on so it is difficult to exploit, other than reverse engineering the password logic.
    

\*\*Statically Analyzing results: \*\*

Tried to find possible function names, system calls and API’s used by strings

Output of Strings:

**#strings crckme-01**

```plaintext
/lib64/ld-linux-x86-64.so.2 
libc.so.6 
strncmp 
__stack_chk_fail 
strlen 
tcsetattr 
read 
malloc 
tcgetattr 
sleep 
__cxa_finalize 
__libc_start_main 
write 
GLIBC_2.4 
GLIBC_2.2.5 
_ITM_deregisterTMCloneTable 
__gmon_start__ 
_ITM_registerTMCloneTable 
AWAVI
AUATL 
[]A\A]A^A_ 
Crack me! 
Password: 
+ Checking Password 
OK. 
Incorrect. 
- Try again. 
+ DONE 
;*3$" 
GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
```

All is all that can be seen about the binary.

Things we can infer: (First thoughts about the binary)

Does strncmp possibly of inputted password and some string generated inside the code

Read, may be used to read from user from console

Malloc – maybe we are allocating buffer for something, not sure at this point.

– Sleep – this was to insert some delay while password was verified.

– Write to write some data somewhere

– Crack me was the string when you started the program

– Password was the prompt to enter the password

– Tcgetattr – may be used to get current setting of some console/terminal

– +Checking Password, was outputted when password was being verified(May be)

– Tcsetattr – may be used to change tty setting for some terminal

– OK – This is what we are looking for

– Incorrect – This is what I got most of the times before this was solved

– DONE – Signalled end of program

Next this was to identify the program flow, meaning what the program actually did. \*\*#strace ./crckme-01 \*\*

#strace ./crckme-01

\*\*Things we can infer about the flow of the program and general observations: \*\*

```plaintext
execve("./crckme-01", ["./crckme-01"], 0x7ffe09c0a660 /* 43 vars */) = 0 
brk(NULL) = 0x561a8b28f000 
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) 
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) 
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 
fstat(3, {st_mode=S_IFREG|0644, st_size=152566, ...}) = 0 
mmap(NULL, 152566, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f7f3f24f000 
close(3) = 0 
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)  
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000,\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1808440, ...}) = 0 
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7f3f24d000 mmap(NULL, 1821408, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f7f3f090000 
mmap(0x7f7f3f0b2000, 1335296, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f7f3f0b2000 mmap(0x7f7f3f1f8000, 307200, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x168000) = 0x7f7f3f1f8000 mmap(0x7f7f3f243000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b2000) = 0x7f7f3f243000 mmap(0x7f7f3f249000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f7f3f249000 close(3) = 0 
arch_prctl(ARCH_SET_FS, 0x7f7f3f24e4c0) = 0
mprotect(0x7f7f3f243000, 16384, PROT_READ) = 0 
mprotect(0x561a8a8de000, 4096, PROT_READ) = 0 
mprotect(0x7f7f3f29c000, 4096, PROT_READ) = 0 
munmap(0x7f7f3f24f000, 152566) = 0 
write(1, "Crack me!\n\n", 11Crack me! 
) = 11 
write(1, "Password:", 9Password:) = 9 
brk(NULL) = 0x561a8b28f000 
brk(0x561a8b2b0000) = 0x561a8b2b0000 
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0 
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0 
ioctl(0, SNDCTL_TMR_CONTINUE or TCSETSF, {B38400 opost isig icanon -echo ...}) = 0 
ioctl(0, TCGETS, {B38400 opost isig icanon -echo ...}) = 0 
read(1, "khjasdasd\n", 32) = 10 
ioctl(0, TCGETS, {B38400 opost isig icanon -echo ...}) = 0 
ioctl(0, SNDCTL_TMR_CONTINUE or TCSETSF, {B38400 opost isig icanon echo ...}) = 0 
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0 
write(1, "\n+ Checking Password ", 21 
+ Checking Password ) = 21 
write(1, ".", 1.) = 1 
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffc062d4430) = 0 
write(1, ".", 1.) = 1 
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffc062d4430) = 0 
write(1, ".", 1.) = 1 
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffc062d4430) = 0 
write(1, ".", 1.) = 1
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffc062d4430) = 0 
write(1, ".", 1.) = 1 
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffc062d4430) = 0 
write(1, "\n Incorrect.\n- Try again.\n", 26 
Incorrect. 
- Try again. 
) = 26 
write(1, "+ DONE\n", 7+ DONE 
) = 7 
exit_group(0) = ? 
+++ exited with 0 +++
```

– Program starts by forking from bash shell process

– Prints “Crack Me” on stdout (1)

– Prints “Password:” as prompt for user to insert his password

– Write is the function that does so with “write(1, “”, strlen(string) + 1); (+1 for ‘\\0’)

– We have an ioctl call as seen from above output, but as per investigation and strings output, no ioctl call is present in application, rather, tcgetattr is used with termios structure to be filled. Looks like it is taking stdin (0) line .

– Then the code blocks at read system call waiting for user input. ( read(1, “khjasdasd\\n”, 32) – size of buffer to hold password entered by us is 32 – may be the malloc earlier was used to create this buffer dynamically ? )

– Tcsetattr is called with some change in line settings, like echo is turned off and is set for stdin (0)

– Then the program goes into verification of password by saying “+ Checking Password” , then prints a “.” Every 1 sec (nanosleep({tv\_sec=1, tv\_nsec=0}, 0x7ffc062d4430))

– Furthermore, the program jumps to some kind of password verification mechanism and then goes out print corresponding success “OK” or Incorrect – Try again on console.

\*\*Diving into Program assembly code: \*\*

First I tried to reverse engineer the program with just GDB and it was really painful without some kind of debugging information in the binary itself, all I could is try to reverse based on hex addresses and identifying function start and end addresses, classify or name them, identify points of interest. It was getting really difficult.

Some futile attempts can be seen here.

Then I said let’s bring out the machine gun → IDA Pro, and yeah.

\*\*IDA – The Saviour \*\*

After loading the program into IDA and number of things sorted out real fast.

– Program assembly addresses got converted into function names

– All functions were properly quantized, meaning, function start and end was easy to understand

– IDA assigned names to variables which made it easier to follow

– Program logic was evident

– Data and Text Sections were properly formatted.

Let’s see what I mean:

Main Function now looked like this:

![](https://lh3.googleusercontent.com/g_Cwaz7ogUGIqgvF9ntrmst1pJScRSp6SIYtdtumQhp7lcHm4ORMoYEqoT7OM5Hz_MfV3x6FiQsPrU3damanfnQSnk3RzrN9GTm_C45tbwlnavTsgoAuS8QJIdFXy_i3TA4eq9ANRJ2Tn2RcUNyRUDg align="left")

As we predicted from strac flow, it seemed to match correctly with the graph from IDA. Now we needed to understand working of the other functions.

Password Verification logic in main: This routing verifies the password entered by user using a predefined value and jumps to corresponding conditions based on the outcome.

![](https://lh6.googleusercontent.com/_oEe6jDmaRvd_ULYZq4kU2ys4cEwkT5G4MIHkpD8vIx8WtPUD2YC9zKGMqhjn-VdCBtGQPM_TgssIrZdYHRsJDR9qFwbjy6chJghVcS0wyW44TS-DGqHc1LDK5tI3BgIWhT7e8ns1J2UGQ7uOM55780 align="left")

Write Function: This function writes the output to the console.

![](https://lh4.googleusercontent.com/NHq7iOa3GJJDizp4EM_L72ScyPULwZuU46xnrimQq3cUNX0sv3VlRtQF4IZHAc-vvxkFfyy7OTI9LhTyBt8bZKScjDq5iFdugyegd5SPDVIdw_rMKRRvjqIlXmD3KzEmXhsp73DLPYm-uKyulhO5clc align="left")

Termios Structure:

– This function defines a local variable of type struct termios and then passes it to tcgetattr function,which fills this structure with current tty values of stdin.

– Next it saves all the values into local variables of another function, changes the value of c\_lflag and then does a tcsetattr

![](https://lh6.googleusercontent.com/-AAsOZ4cF9PJdTHjz7nVFnskOnD5s8Xk5yEjYExOhj6zIBvER6aTzen3vKazI2OcLnrZ7y_TbEE2tUfWW5sEbiTTm77Z-WFM2t44HR1e7Cbac-Sx1CJxJHQIi1OJB-Q-7P6nBms-sydCitp9jidWVYE align="left")

– Then it goes and asks the user to enter the password from read function call, and supplies the buffer allocated from the previous malloc call.

Termios Structure Contd :

![](https://lh4.googleusercontent.com/tgYKn-ggFhxWg0JKI_5091i9Sh0a-tqW1mlyf88D2SZ2VKUCW5xehPMNEN_s0GUaabXzCwvDh6UFoX_hG2LoaveDmiqGKeTLOyqbRRgn7u4jA31Ic_bFoGRIHd_Cqwb5z7ruqCVcWlvxqW8fKHMGQnE align="left")

Possible Encrytion Routine:

![](https://lh4.googleusercontent.com/-IsWPybYrqszBQTB49Z2DV4Em19V67hz1ZSCgtaKV2zm0HnTqdKqTRf-IbR180h1fUxfTVi381_9K8fo8hqnOe91KatMbJBFhbhDH2nZ5ZlE4tE_cuQ3fAiU1nG81c2HKbVnFbc_2STh29IJnaKsTos align="left")

After running each and every instruction in GDB and then correlating their actions per the above graphs, the program flow was pretty evident.

Final Blow:

– It was found that there is a strncmp which compares two strings, s1 and s2, s2 is the string which never changes and is the value against which our password is compared, hence, and it must be our password.

– S1 is the string entered by us, over which some kind of logic is performed as seen from above diagrams. – As we cannot see the value of s2 in any of the code, it must be dynamically generated by some algorithm at runtime, which finally appears in the string s2 right before the comparison happens.

– Actually password cracking happened after adding a breakpoint at the correct instruction of comparison right before the strncmp call, which GDB peda shows the arguments to (check video)

–

![](https://lh4.googleusercontent.com/g3cRFdDfPKfo5irUwxDzSimhVdVA96eSpg4d5q2MEQhldER-cYPHwIxBuYh9Jbs7j58T_fgAsNKTj3XDwOJnK20BXAl1mF24ZDSOI7urAXprSOpO1be6qSBUn-xB7ytqMHy6NdP4E18ZNZfliMyqLa4 align="left")

– Now let’s see how it looks in GDB

![](https://lh5.googleusercontent.com/IHbsFihNmKBA_pY6Ip8RsBnwTKZ0yltYzdnqsxJKxtJ3bfdlHxg6g5ox99-bLDARM1v5KdPH4bHpJDFd9Nt3cCSDVuw709U8e5hF9pJS6H409GPcWlvSUzbJ4mIHHrpRvJxVfZG5IBIMQUUJrhJYGyI align="left")

– Argument 0 and argument 1 to the strncmp function shown above

– Arg\[0\] – Password Entered by us – char \*s1 for strncmp function

– Arg\[1\] – Password Dynamically generated by some seed value inside the code – char \*s2 for strncmp function

Now let’s see the working use case

![](https://lh3.googleusercontent.com/nIXmUSXnal24K8Z2StFcW96FJ0iVMb8MdvycFutJc_DNoOygcbIuPDujLoEHUvhSmVBaqsRDcQYakg5ka2OpaVILnz8LH33CQvxZflO1975VNkLj9SDL_chQHRZpjS99QNdOP89GGPpTfbI2b3868Ls align="left")

Now both argv\[0\] and argv\[1\] match.

Initially I tried to reverse engineer the Encryption logic, but it was taking too much time.

Some points on the Encryption or Password generation routing:

– I observed these values for a long time

– All 32bits or 8bytes of the password had to match

– If I entered ‘A’ as the first character, it would map to 0x62 always, but if I entered it as a second character of the password it generated some new value

– It started me thinking that the encryption routing was something like the Enigma machine, as for every entered character it would generate different output if the position of entered character was different than what was in the previous run, this scared me. But after several trials. (It gave a feel like Enigma, but not really Enigma).

– I made relationship map of every character to its corresponding Hex character, for every position.

– After repeating this process, I could map all character to its corresponding hex value and solve the challenge. – So the logic to generate password may be some kind of rotational cipher which used a seed value and may be, is a function of current position. Couldn’t be sure about this.

Password was “ABCtEFG”

A Small video is available here for the bare bones approach.

%[embed/UohfjrEpX1Q] 

Will Reverse Engineer for Food – Give me some GDB

I was asked to send response to this challenge in 48 hours – It was quite ecstatic when I was somehow able to do it under 12 hours.

Ghidra was Not available when i attempted this challenge.

## The actual Algorithm – It was just a simple xor with some constant value

In Continuation to the Linux Reverse Engineering challenge – The Encryption Algorithm is nothing but an Xor operation between the Password in Hex Format, initialized and seen in Data Segment of the Binary.

![](https://autohackin.wordpress.com/wp-content/uploads/2023/08/screenshot-2023-08-07-at-12.56.47-pm.png?w=841 align="left")

Figure 1: Read-Only Initialized data segment of the program (unk\_C78 from IDA Pro, show the password here)

The Encryption Algorithm: – Performs Xor Operation on characters entered by us from console and stores it in s1 (Figure 2)

![](https://autohackin.wordpress.com/wp-content/uploads/2023/08/screenshot-2023-08-07-at-12.57.09-pm.png?w=822 align="left")

Figure 2: Main Function Program Flow

– It takes the password from data section and performs an XOR with the password entered by user by taking 1bytes at a time, reversing it, shifting it to get the endianess and then send it for comparison.

![](https://autohackin.wordpress.com/wp-content/uploads/2023/08/screenshot-2023-08-07-at-12.57.38-pm.png?w=531 align="left")

– XOR operation can be seen below(Figure 3)

Figure 3: Rotation and Xor Logic (esi and ecx, contain our xor’ed seed value and user entered seed value)

![](https://autohackin.wordpress.com/wp-content/uploads/2023/08/screenshot-2023-08-07-at-12.58.07-pm.png?w=412 align="left")

Seed Value XOR Key Password

Figure 4: Formula

Hence,

Seed Value = 0x2389F2C26D3EC7B2 (As available in data section)

Little Endian = 0xB2C73E6DC2F28923

Seed Value when Xor’ed with password = 0xB2807828B6B1CB62 (Little Endian)

Hence the password should be, Password = Seed Value Xor’ed with 0xB2807828B6B1CB62 (As seen in Figure 5 – retrieved via Dynamic Analysis)

![](https://autohackin.wordpress.com/wp-content/uploads/2023/08/screenshot-2023-08-07-at-12.59.00-pm.png?w=847 align="left")

![](https://autohackin.wordpress.com/wp-content/uploads/2023/08/screenshot-2023-08-07-at-12.59.29-pm.png?w=828 align="left")

Figure 5: Final BreakPoint Output

Therefore, password = 0xB2C73E6DC2F28923 XOR 0xB2807828B6B1CB62 = 0x47464574434241 (Little Endian) So if we convert this into Ascii then we get “GFEtCBA”, now we reverse this to get into console format, as data is stored in reverse order for X86 systems, we get the PASSWORD = “ABCtEFG”.

It was fun to do this before the allocated time.

Unfortunately, I was offered the job but due a hiring freeze all the offers at the time were taken back. I stayed in touch with the technical manager for 4-5 months, then he also left that position so this did not materialise.

I’m Still hungry and foolish now \\m/

%[https://youtu.be/8EMk8OEwvPg]
