niel’s very easy crackme1 solved
grats to crp- for solving! greets to grainne for inspiring me to write this crackme.
well, in a nutshell, this crackme relies on some simple checks to trigger a cracked condition. i wrote it in c one very hot day while recovering from a sprained ankle. i’ll first explain some nonsense and after that, i’ll finally post the crackme listing for the benefit of everyone.
a) first it checks for a 5 byte file LiuYiFei_143 in the current directory
b) second, it does a simple checksum on argv[1].
here are a few assumptions/ideas that led me to design my crackme the way it’s designed now.
a) anti-ptrace protections are placed in an easily found area, that is, inside a function like main().
b) a lazy cracker can simply override weak functions like ptrace() and other library calls.
c) the strings utility can be used to print strings and i don’t want any cross references easily seen.
d) objdump and other “bfd” (binary file descriptor library) based tools rely on section headers.
e) luring them away from the real check as much as possbile sounds like a good plan.
f) i still don’t know enough to make a very hard crackme.
i tried approaching assumption a, “anti-ptrace in an easily found area” by using constructor attributes. gcc manual. what happens is that before control reaches the main() function, many other functions are called.. i won’t go into details about all those functions except the contructor functions. they are called just before main.
because of this merit, i thought it would be a good place to put protections. in comes your PTRACE_TRACEME trick. but, ptrace is a weak function. and any student of reverse engineering can simply go past this if he uses LD_PRELOAD. in comes the second protection. prevention of LD_PRELOAD overrides. this is for assumption b.
if there are constructor functions that resolve before main() there are also destructors that resolve after main(). i placed the check for the crack condition here. as they resolve automatically, i dont need to call them explicitly. so you wont find any actual link for the primary check condition and the the function to print the string cracked in the main() function.
now, for assumption c, the “strings” utility is a handy tool to print printable characters in files. it’s always used for preliminary forensics so i wanted to raise the bar a bit higher by preventing the “strings” utility from printing strings like the filename “LiuYiFei_143″, the string “Cracked!” and the string “LD_PRELOAD”. winding them with a simple xor operation did the trick. i had to therefore, unwind these strings before actually using them so i also included a string decryptor function. try running “strings” on the crackme to see for yourself.
assumption d is a bonus. i only corrupted the elf header sections, not the section themselves! i’ll remember to do that next time! also, i’ll really consider doing static linkage at the cost of binary size.
assumption e, is self explanatory.
idea f is impossible to achieve.
if you’ve reached this far! you must really be bored now! here’s the source code for my crackme as promised.
/* my first crackmes.de submission
* niel anthony acuna
*/
#include <stdio.h>
#include <sys/ptrace.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
char status;
static void ptrace_ctor() __attribute__((constructor));
static void crack_ctor() __attribute__((constructor));
static void crack_dtor() __attribute__((destructor));
static void unwind_string(char *string, int len);
static void crack_main();
static void unwind_string(char *string, int len)
{
int i;
for (i=0;i<len ; i++)
string[i] ^= i;
string[len] = '\0';
}
static void crack_ctor()
{
struct stat stc;
char string[] = {0x4c, 0x68, 0x77, 0x5a, 0x6d, 0x43, \
0x63, 0x6e, 0x57, 0x38, 0x3e, 0x38};
unwind_string(string, 12);
if (!stat(string, &stc)) {
if (stc.st_size == 5) status = 1;
return ;
}
status = 0;
}
static void ptrace_ctor()
{
char *pload;
char string[] = {0x4c, 0x45, 0x5d, 0x53, 0x56, 0x40, \
0x4a, 0x48, 0x49, 0x4d, ' ', ' '};
unwind_string(string, 10);
pload = getenv(string);
if (pload) exit(0);
if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) exit(0);
}
static void crack_dtor()
{
char string[] = {0x63, 0x73, 0x63, 0x60, 0x6f, 0x60, \
0x62, 0x26, ' ', ' ', ' ', ' '};
unwind_string(string, 8 );
if ((status & 3) == 3) printf("%s\n", string);
}
static void crack_main(char *serial)
{
int a, b, c;
if (strlen(serial) < 10) exit(0);
a = serial[0];
b = serial[2];
c = serial[4];
if ((a+b+c) != 330) exit(0);
status |= 0x2;
}
int main(int argc, char **argv)
{
printf("..:: easy crackme1 by niel anthony acuna ::..\n");
if (argc < 2 ) return(0);
crack_main(argv[1]);
return 0;
}
so how to go about the crackme?
create a 5 byte file “LiuYiFei_143″ and input an argv[1] with a length of no less than 9 that satisfies the checksum - first three odd characters, when added must be equal to 330.
also view crp-’s solution here.
cheers,
- niel
