IOLI 0x06
nearly a routine to check this binary (not complete output in the following):
$ rabin2 -z ./crackme0x06
[Strings]
nth paddr vaddr len size section type string
-------------------------------------------------------
0 0x00000738 0x08048738 4 5 .rodata ascii LOLO
1 0x00000740 0x08048740 13 14 .rodata ascii Password OK!\n
2 0x0000074e 0x0804874e 20 21 .rodata ascii Password Incorrect!\n
3 0x00000763 0x08048763 24 25 .rodata ascii IOLI Crackme Level 0x06\n
4 0x0000077c 0x0804877c 10 11 .rodata ascii Password:
$ rabin2 -I ./crackme0x06
arch x86
baddr 0x8048000
bintype elf
bits 32
compiler GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)
crypto false
endian little
havecode true
lang c
machine Intel 80386
maxopsz 16
minopsz 1
os linux
static false
va true
and analyze it then decompile main
[0x08048400]> pdd@main
/* r2dec pseudo code output */
/* ./crackme0x06 @ 0x8048607 */
#include <stdint.h>
int32_t main (int32_t arg_10h) {
int32_t var_78h;
int32_t var_4h;
// adjusting stack
eax = 0;
eax += 0xf;
eax += 0xf;
eax >>= 4;
eax <<= 4;
// main logic
printf ("IOLI Crackme Level 0x06\n");
printf ("Password: ");
eax = &var_78h;
scanf (0x8048787, eax);
eax = arg_10h;
eax = &var_78h;
check (eax, arg_10h);
eax = 0;
return eax;
}
main has 3 arguments argc, argv, envp
, and this program is compiled with GCC, so the stack should be like this :
[esp + 0x10] - envp
[esp + 0x0c] - argv
[esp + 0x08] - argc
[esp + 0x04] - return address
enter the check()
and decompile it. this function is different from 0x05 now. but they still have similar code structure.
int32_t check (char * s, int32_t arg_ch) {
char * var_dh;
uint32_t var_ch;
uint32_t var_8h;
int32_t var_4h;
char * format;
int32_t var_sp_8h;
var_8h = 0;
var_ch = 0;
do {
eax = s;
eax = strlen (eax);
if (var_ch >= eax) {
goto label_0;
}
eax = var_ch;
eax += s;
eax = *(eax);
var_dh = al;
eax = &var_4h;
eax = &var_dh;
sscanf (eax, eax, 0x804873d);
edx = var_4h;
eax = &var_8h;
*(eax) += edx;
if (var_8h == 0x10) {
eax = arg_ch;
eax = s;
parell (eax, arg_ch);
}
eax = &var_ch;
*(eax)++;
} while (1);
label_0:
printf ("Password Incorrect!\n");
return eax;
}
Correct the sscanf
part and parell
part, both of them were generated incorrectly:
int32_t check (char * s, void* envp)
{
var_ch = 0;
var_8h = 0;
for (var_ch = 0; var_ch < strlen(s); ++var_ch)
{
var_dh = s[var_ch];
sscanf(&var_dh, %d, &var_4h); // read from string[var_ch], store to var_4h
var_8h += var_4h;
if(var_8h == 0x10)
parell(s, envp);
}
printf("Password Incorrect!\n");
return 0;
}
no more info, we have to reverse parell()
again.
#include <stdint.h>
uint32_t parell (char * s, char * arg_ch) {
sscanf (s, %d, &var_4h);
if (dummy (var_4h, arg_ch) == 0)
return 0;
for (var_bp_8h = 0; var_bp_8h <= 9; ++var_bp_8h){
if (var_4h & 1 == 0){
printf("Password OK!\n");
exit(0);
}
}
return 0;
}
well, there is a new check condition in parell()
-- dummy (var_4h, arg_ch) == 0
. then reverse dummy!
[0x080484b4]> pdd@sym.dummy
/* r2dec pseudo code output */
/* ./crackme0x06 @ 0x80484b4 */
#include <stdint.h>
int32_t dummy (char ** s1) {
int32_t var_8h;
int32_t var_4h;
char * s2;
size_t * n;
var_4h = 0;
do {
eax = 0;
edx = eax*4;
eax = s1;
if (*((edx + eax)) == 0) {
goto label_0;
}
eax = var_4h;
ecx = eax*4;
edx = s1;
eax = &var_4h;
*(eax)++;
eax = *((ecx + edx));
eax = strncmp (eax, 3, "LOLO");
} while (eax != 0);
var_8h = 1;
goto label_1;
label_0:
var_8h = 0;
label_1:
eax = 0;
return eax;
}
looks like a loop to process string. we can beautify it.
[0x080484b4]> pdd@sym.dummy
/* r2dec pseudo code output */
/* ./crackme0x06 @ 0x80484b4 */
#include <stdint.h>
int32_t dummy (char ** s1) {
for (var_4h = 0; strncmp(s1[var_4h], "LOLO", 3) != 0; var_4h++){
if (s1[i] == NULL)
return 0;
}
return 1;
}
There are 3 constraints to crackme_0x06:
- Digit Sum
- Odd Number
- should have an environment variable whose name started with "LOL".
$ ./crackme0x06
IOLI Crackme Level 0x06
Password: 12346
Password Incorrect!
$ export LOLAA=help
$ ./cracke0x06
IOLI Crackme Level 0x06
Password: 12346
Password OK!