The Loader
The loader
Description
Level: 3 Score 30 Category reversing
I've seen that in malware before. There must be something inside
Link: SecurityValley/PublicCTFChallenges/reversing/the_loader
Analysis
└─$ file ./crackme-03
./crackme-03: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=ea682044081d67c91cecfc045521f43e40bd9330, for GNU/Linux 3.2.0, not stripped
└─$ chmod u+x crackme-03
└─$ ./crackme-03
0.\0/
/ \
Wouldn't it be cool to be able to look into the memory?
1./0\
/ \
Wouldn't it be cool to be able to look into the memory?
...
So let't take a look inside the program.
I'll be using Ghidra and ghidra_auto script to quickly create a project.
└─$ ghidra_auto --temp crackme-03
Most programs have a main
function which is essentially start of the program. To view main function navigate to Symbol Tree (tab on the left corner) > Functions > main
data = &DAT_00102018;
counter = 0;
do {
adjustCounter(&counter);
hopStr = getHop(counter);
printf("%d.%s \n\nWouldn\'t it be cool to be able to look into the memory?\n\n"(ulong)counter, hopStr);
huuu(data);
counter = counter + 1;
sleep(1);
} while( true );
Note: I have changed variables for more readability (by using lowercase L
)
When we ran the program we saw that output was simple printf, so we can ignore it. What's interesting is huuu(data)
. data
is a memory which gets loaded from program. Let's take a look at huuu
(double click to jump to pseudo source code)
void huuu(undefined8 data) {
undefined8 loadedMemory;
undefined8 loadedMemorySize;
loadedMemory = load_s_mem(data,0xfe);
loadedMemorySize = get_str_size(loadedMemory);
unload_mem(loadedMemory,loadedMemorySize);
return;
}
Looks like the function does absolutely nothing, it loads the memory and unloads it. But what does it load?
void * load_s_mem(void *data, byte key) {
size_t __size;
void *__dest;
int i;
__size = get_str_size(data);
__dest = malloc(__size);
memcpy(__dest,data,__size);
for (i = 0; (ulong)(long)i < __size; i = i + 1) {
*(byte *)((long)__dest + (long)i) = *(byte *)((long)__dest + (long)i) ^ key;
}
*(undefined *)(__size + (long)__dest) = 0;
return __dest;
}
What actually happens in this function? and why does the code look so scary?
Simple Answer: Each item in data gets xor-ed with the key and finally is returned
Long Answer:
__dest = malloc(__size);
Memory is getting allocated with size of datamemcpy(__dest,data,__size);
Data is getting copied inside__dest
(buffer)Standard
for
loop*(byte *)((long)__dest + (long)i)
(long)__dest + (long)i
moves the__dest
base pointer byi
bytes(byte *)
then pointer is type casted tobyte
pointer (malloc
return void pointer)*
finally the pointer is getting dereferenced and byte value stored at that address gets retrieved .
On the right side of expression byte value is getting XOR-ed with param_2 (key)
^
= XOR
*(undefined *)(__size + (long)__dest) = 0;
Finally the last byte is getting assigned null byte to mark the end of buffer.
Ok, XOR is simple to reverse. We have key, but we dont have the data. Ghidra makes it easy to view some regions of memory. Let's go back to main.
data = &DAT_00102018;
Note: Double click &DAT_* to jump to address

Extracted data: ad9b9da89f928589caa7a1c9cea196ca8cbaa1b8ceaca1a7ceab830000000000
Solution

Last updated