- In this note there are multiple IDEs and tools to perform reversing
- Check IDA ๐ notes
Analyzing functions
- Sometimes you can find flags inside a function in plain text just by observing the Decompiler (open in
Window >> Decompiler
):
- If you find a lot of variables predefined on a function as follows:
- You can see that the first one is a
char
whereas the last one is apointer
. If we look at the dis-assembler section, we can get an idea about how these variables are added to the stack. We can clearly see a reverse order.
- The last char pointer which isย local_8ย is the first one added to the stack as follows.
- We can see that each variable is assigned a value as follows. The first char variable which isย local_2cย is assigned to โFโ and all the undefined variables are assigned hex values.
- So now, we know that these hex values should be ASCII characters because each value is exactly one byte.
- If you remember, we found that the last variable defined which is local_8, is a character pointer. Now, it is assigned to be aย hash of the stringย starting at the memory address of the variableย char local_2c, as follows.
- This means that it will hash the entire string fromย bottom of the stackย toย top. Thus, we need toย convertย each hex character assigned for the variables afterย local_2cย into ASCII characters in order to obtain the string that is being hashed.
- You can useย hex-to-ascii converterย for this purpose. You will be given the flag in ASCII.
- As you can see, there are some external functions called such asย FindResourceA()ย andย LoadStringsA(). When we look at the assembly code, we can come across that FindResourceA() is from the Kernel32 Library and LoadStringsA() is from User32 Library.
- What LoadStringsA() does is that it loads the string from its reference and stores at the variableย local_4a4.ย So, let us find the reference.
- As shown in the above entry function, we know thatย 0x110ย hex value parameter is the identifier for the string in LoadStringsA() function call.
- Hex 0x110 = Decimalย 272
- Openย Defined Stringsย from Ghidra ribbon (Window >> Defined Strings). Scroll down and you will come across a FLAG table which contains many flags.
- Take a look at the equivalent code on dis-assembler and you will find-out theย string-IDย at the far right corner in decimal. So, scroll down until you find the string-ID 272.
Using gdb when finding strcmp() function
gdb file
(gdb) info functions
0x00000000004004b0 _init
0x00000000004004e0 puts@plt
0x00000000004004f0 __stack_chk_fail@plt
0x0000000000400500 printf@plt
0x0000000000400510 __libc_start_main@plt
0x0000000000400520 strcmp@plt
0x0000000000400530 __gmon_start__@plt
0x0000000000400540 _start
0x0000000000400570 deregister_tm_clones
0x00000000004005a0 register_tm_clones
0x00000000004005e0 __do_global_dtors_aux
0x0000000000400600 frame_dummy
0x000000000040062d get_pwd
0x000000000040067a compare_pwd
0x0000000000400716 main
0x0000000000400760 __libc_csu_init
0x00000000004007d0 __libc_csu_fini
0x00000000004007d4 _fini
- We are interested in the
strcmp@plt
function - Set a breakpoint at the memory address of this function and run the binary in gdb with some test input:
(gdb) b *0x0000000000400520
(gdb) run test
- The binary is executed until it hits the breakpoint as seen in the image above. Next, I can view the current state of the registers with gdb:
(gdb) info registers
rax 0x7fffffffdd50 140737488346448
rbx 0x7fffffffdea8 140737488346792
rcx 0x11 17
rdx 0x7fffffffe245 140737488347717
rsi 0x7fffffffe245 140737488347717
rdi 0x7fffffffdd50 140737488346448
rbp 0x7fffffffdd70 0x7fffffffdd70
rsp 0x7fffffffdd38 0x7fffffffdd38
r8 0x4007d0 4196304
r9 0x7ffff7fcfaf0 140737353939696
r10 0x7ffff7fcb858 140737353922648
r11 0x7ffff7fe1f80 140737354014592
r12 0x0 0
r13 0x7fffffffdec0 140737488346816
r14 0x0 0
r15 0x7ffff7ffd000 140737354125312
rip 0x400520 0x400520 <strcmp@plt>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
- Looking at the output above I can see the names of the registers, the registers value in hexadecimal format and the registers value in the format gdb thinks most appropriate (hex for pointers, decimal for the others). I can see that the general purpose registers rax and rdx have memory address values. I can use gdb to print the strings at these addresses:
(gdb) x/s 0x7fffffffdd50
Not known file architecture
- If you perform
file whatever.whatever
and gives you this:
0x41haz-1640335532346.0x41haz: ELF 64-bit MSB *unknown arch 0x3e00* (SYSV)
-
unknown arch 0x3e00
means that the file has no architecture -
MSB
refers to the endianness of the file. Most significant bit first means that the most significant byte (bit) of multi-byte data is stored at the lowest memory address. This endianness is commonly used in big-endian architectures. -
SYSV
, ABI (Application Binary Interface) used in the ELF file. โSYSVโ stands for System V, which is a standard Unix ABI used in many Linux/Unix systems. -
Open the file in a hex editor like:
hexeditor
or online hexeditor and follow this trick:
- Edit the sixth bit on the code:
- Change the
02
by01
:
- Now it can be detected:
$ file 0x41haz-1640335532346.0x41haz
0x41haz-1640335532346.0x41haz: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=6c9f2e85b64d4f12b91136ffb8e4c038f1dc6dcd, for GNU/Linux 3.2.0, stripped
Personal advices
- If you find a code (maybe encrypted), try to reverse it because sometimes thatโs the answer :D
Searching for env variables
ltrace FILE
getenv("admin") = nil
puts("Not an Admin"Not an Admin
) = 13
+++ exited (status 0) +++
- In this case, you can create an
admin
environment variable and set it to 1:
export admin=1