115 IOLI 0x05
check again, it uses scanf()
to get our input and pass it to check()
as parameter.
[0x080483d0]> pdd@main
/* jsdec pseudo code output */
/* ./crackme0x05 @ 0x8048540 */
#include <stdint.h>
int32_t main (void) {
int32_t var_78h;
int32_t var_4h;
= 0;
eax += 0xf;
eax += 0xf;
eax >>= 4;
eax <<= 4;
eax ("IOLI Crackme Level 0x05\n");
printf ("Password: ");
printf = &var_78h;
eax (0x80486b2, eax); // 0x80486b2 is %s
scanf = &var_78h;
eax (eax);
check = 0;
eax return eax;
}
the check() function:
/* jsdec pseudo code output */
/* ./crackme0x05 @ 0x80484c8 */
#include <stdint.h>
int32_t check (char * s) {
char * var_dh;
uint32_t var_ch;
uint32_t var_8h;
int32_t var_4h;
char * format;
int32_t var_sp_8h;
= 0;
var_8h = 0;
var_ch do {
= s;
eax = strlen (eax);
eax if (var_ch >= eax) {
goto label_0;
}
= var_ch;
eax += s;
eax = *(eax);
eax = al;
var_dh = &var_4h;
eax = &var_dh;
eax (eax, eax, 0x8048668); // 0x8048668 is %d
sscanf = var_4h;
edx = &var_8h;
eax *(eax) += edx;
if (var_8h == 0x10) {
= s;
eax (eax);
parell }
= &var_ch;
eax *(eax)++;
} while (1);
:
label_0("Password Incorrect!\n");
printf return eax;
}
The same, we can write our own C-like pseudo code.
#include <stdint.h>
int32_t check(char *s)
{
= 0;
var_ch = 0;
var_8h for (var_ch = 0; var_ch < strlen(s); ++var_ch)
{
= s[var_ch];
var_dh (&var_dh, %d, &var_4h); // read from string[var_ch], store to var_4h
sscanf+= var_4h;
var_8h if(var_8h == 0x10)
(s);
parell}
("Password Incorrect!\n");
printfreturn 0;
}
The if condition becomes var_8h == 0x10
. In addition, a new function call - parell(s)
replace the printf("password OK")
now. The next step is to reverse sym.parell.
[0x08048484]> s sym.parell
[0x08048484]> pdd@sym.parell
/* jsdec pseudo code output */
/* ./crackme0x05 @ 0x8048484 */
#include <stdint.h>
uint32_t parell (char * s) {
int32_t var_4h;
char * format;
int32_t var_8h;
= &var_4h;
eax = s;
eax (eax, eax, 0x8048668);
sscanf = var_4h;
eax &= 1;
eax if (eax == 0) {
("Password OK!\n");
printf (0);
exit }
return eax;
}
the decompiled code looks well except the sscanf()
function. It can be easily corrected by checking the assembly code.
.parell (int32_t arg_8h);
/ 68: sym; var int32_t var_4h @ ebp-0x4
| ; arg int32_t arg_8h @ ebp+0x8
| ; var int32_t var_sp_4h @ esp+0x4
| ; var int32_t var_8h @ esp+0x8
| push ebp
| 0x08048484 55 mov ebp, esp
| 0x08048485 89e5 sub esp, 0x18
| 0x08048487 83ec18 lea eax, [var_4h]
| 0x0804848a 8d45fc mov dword [var_8h], eax
| 0x0804848d 89442408 . mov dword [var_sp_4h], 0x8048668 ; [0x8048668:4]=0x50006425 %d
| 0x08048491 c74424046886mov eax, dword [arg_8h]
| 0x08048499 8b4508 mov dword [esp], eax
| 0x0804849c 890424 .imp.sscanf ; int sscanf(const char *s, const char *format, ...)
| 0x0804849f e800ffffff call sym ....
The mov dword [esp], eax
is the nearest instruction to sscanf (and it’s equivalent to a push instruction). It stores the string ‘s’ to the stack top (arg1). mov dword [var_sp_4h], 0x8048668
push ‘%d’ as arg2 into stack. var_8h (esp + 0x8) which keeps the address of var_4h is the arg3.
Finally we have the corrected pseudo code:
uint32_t parell (char * s) {
(s, %d, &var_4h);
sscanf if ((var_4h & 1) == 0) {
("Password OK!\n");
printf (0);
exit}
return 0;
}
Now there are 2 constraints:
- Digit Sum is 16 (0x10)
- Must be an odd number (1 & number == 0)
The password is at our fingertips now.
./crackme0x05
IOLI Crackme Level 0x05
Password: 88
Password OK!
./crackme0x05
IOLI Crackme Level 0x05
Password: 12346
Password OK!
we can also use angr to solve it since we have two constraints to the password.