IOLI整理
IOLI crackme
crackme0x05
分析sym.check这个函数
/ (fcn) sym.check 120
| sym.check (char *s);
| ; var char *var_dh @ ebp-0xd
| ; var uint32_t var_ch @ ebp-0xc
| ; var uint32_t var_8h @ ebp-0x8
| ; var int32_t var_4h @ ebp-0x4
| ; arg char *s @ ebp+0x8
| ; var char *format @ esp+0x4
| ; var int32_t var_8h_2 @ esp+0x8
| ; CALL XREF from main (0x8048590)
| 0x080484c8 55 push ebp
| 0x080484c9 89e5 mov ebp, esp
| 0x080484cb 83ec28 sub esp, 0x28 ; '('
| 0x080484ce c745f8000000. mov dword [var_8h], 0
| 0x080484d5 c745f4000000. mov dword [var_ch], 0
| ; CODE XREF from sym.check (0x8048530)
| .-> 0x080484dc 8b4508 mov eax, dword [s] ; [0x8:4]=-1 ; 8
| : 0x080484df 890424 mov dword [esp], eax ; const char *s
| : 0x080484e2 e89dfeffff call sym.imp.strlen ; size_t strlen(const char *s)
| : 0x080484e7 3945f4 cmp dword [var_ch], eax
| ,==< 0x080484ea 7346 jae 0x8048532
| |: 0x080484ec 8b45f4 mov eax, dword [var_ch]
| |: 0x080484ef 034508 add eax, dword [s]
| |: 0x080484f2 0fb600 movzx eax, byte [eax]
| |: 0x080484f5 8845f3 mov byte [var_dh], al
| |: 0x080484f8 8d45fc lea eax, [var_4h]
| |: 0x080484fb 89442408 mov dword [var_8h_2], eax ; ...
| |: 0x080484ff c74424046886. mov dword [format], 0x8048668 ; [0x8048668:4]=0x50006425 ; const char *format
| |: 0x08048507 8d45f3 lea eax, [var_dh]
| |: 0x0804850a 890424 mov dword [esp], eax ; const char *s
| |: 0x0804850d e892feffff call sym.imp.sscanf ; int sscanf(const char *s,| `---> 0x0804852b 8d45f4 lea eax, [var_ch]
| |: 0x0804852e ff00 inc dword [eax]
| |`=< 0x08048530 ebaa jmp 0x80484dc const char *format, ...)
| |: 0x08048512 8b55fc mov edx, dword [var_4h]
| |: 0x08048515 8d45f8 lea eax, [var_8h]
| |: 0x08048518 0110 add dword [eax], edx
| |: 0x0804851a 837df810 cmp dword [var_8h], 0x10
| ,===< 0x0804851e 750b jne 0x804852b
| ||: 0x08048520 8b4508 mov eax, dword [s] ; [0x8:4]=-1 ; 8
| ||: 0x08048523 890424 mov dword [esp], eax
| ||: 0x08048526 e859ffffff call sym.parell
| ||: ; CODE XREF from sym.check (0x804851e)
| `---> 0x0804852b 8d45f4 lea eax, [var_ch]
| |: 0x0804852e ff00 inc dword [eax]
| |`=< 0x08048530 ebaa jmp 0x80484dc
| | ; CODE XREF from sym.check (0x80484ea)
| `--> 0x08048532 c70424798604. mov dword [esp], str.Password_Incorrect ; [0x8048679:4]=0x73736150 ; "Password Incorrect!\n" ; const char *format
| 0x08048539 e856feffff call sym.imp.printf ; int printf(const char *format)
| 0x0804853e c9 leave
\ 0x0804853f c3 ret
程序开始将两个变量置为零,分析发现其中一个用于记步,一个用于累加。执行的次数受输入的字符串的长度制约。
| |: 0x080484ec 8b45f4 mov eax, dword [var_ch]
| |: 0x080484ef 034508 add eax, dword [s]
| |: 0x080484f2 0fb600 movzx eax, byte [eax]
| |: 0x080484f5 8845f3 mov byte [var_dh], al
| |: 0x080484f8 8d45fc lea eax, [var_4h]
| |: 0x080484fb 89442408 mov dword [var_8h_2], eax ; ...
| |: 0x080484ff c74424046886. mov dword [format], 0x8048668 ; [0x8048668:4]=0x50006425 ; const char *format
| |: 0x08048507 8d45f3 lea eax, [var_dh]
| |: 0x0804850a 890424 mov dword [esp], eax ; const char *s
| |: 0x0804850d e892feffff call sym.imp.sscanf ; int sscanf(const char *s, const char *format, ...)
分析这一部分,先将输入的字符串的地址与记步器的值相加。
| |: 0x080484ec 8b45f4 mov eax, dword [var_ch]
| |: 0x080484ef 034508 add eax, dword [s]
| |: 0x080484f2 0fb600
注意此处的dword [s]
是作为参数传递进来的字符串,而字符串变量实际上是字符串起始位置的地址,通过此地址与下标的相加可以得到字符串各个位置的字符的地址。
| |: 0x080484f2 0fb600 movzx eax, byte [eax]
| |: 0x080484f5 8845f3 mov byte [var_dh], al
eax此时存储的是输入的字符串中的某一位的地址,byte[eax]获取了该地址上的那一位字符,利用movzx实现8位向32位赋值,高位补零,将这一位字符存入eax中。然后将al,即刚刚取出来的这一位字符存放在变量var_dh中
| |: 0x080484f8 8d45fc lea eax, [var_4h]
| |: 0x080484fb 89442408 mov dword [var_8h_2], eax ; ...
| |: 0x080484ff c74424046886. mov dword [format], 0x8048668 ; [0x8048668:4]=0x50006425 ; const char *format
| |: 0x08048507 8d45f3 lea eax, [var_dh]
| |: 0x0804850a 890424 mov dword [esp], eax ; const char *s
| |: 0x0804850d e892feffff call sym.imp.sscanf ; int sscanf(const char *s, const char *format, ...)
将参数存储在栈上。依次将目标字符串地址,格式,源字符串存入栈中,接着调用sscanf函数。由于格式化字符串设置为%d,即读取一个十进制数,该过程实现了将数字字符转换为相应的数字的过程。
| |: 0x08048512 8b55fc mov edx, dword [var_4h]
| |: 0x08048515 8d45f8 lea eax, [var_8h]
| |: 0x08048518 0110 add dword [eax], edx
| |: 0x0804851a 837df810 cmp dword [var_8h], 0x10
| ,===< 0x0804851e 750b jne 0x804852b
接下来将读取出来的数字加在累加器上,进行求和,与0x10进行比较,若不等,则进行跳转
| `---> 0x0804852b 8d45f4 lea eax, [var_ch]
| |: 0x0804852e ff00 inc dword [eax]
| |`=< 0x08048530 ebaa jmp 0x80484dc
记步器+1之后进行下一轮循环,即将下一个数字加上。
若求和之后等于0x10,则继续进行检验
| ||: 0x08048520 8b4508 mov eax, dword [s] ; [0x8:4]=-1 ; 8
| ||: 0x08048523 890424 mov dword [esp], eax
| ||: 0x08048526 e859ffffff call sym.parell
| ||: ; CODE XREF from sym.check (0x804851e)
将输入的字符串作为参数传递给sym.parell,sym.parell程序如下:
/ (fcn) sym.parell 68
| sym.parell (char *s);
| ; var int32_t var_4h @ ebp-0x4
| ; arg char *s @ ebp+0x8
| ; var char *format @ esp+0x4
| ; var int32_t var_8h @ esp+0x8
| ; CALL XREF from sym.check (0x8048526)
| 0x08048484 55 push ebp
| 0x08048485 89e5 mov ebp, esp
| 0x08048487 83ec18 sub esp, 0x18
| 0x0804848a 8d45fc lea eax, [var_4h]
| 0x0804848d 89442408 mov dword [var_8h], eax ; ...
| 0x08048491 c74424046886. mov dword [format], 0x8048668 ; [0x8048668:4]=0x50006425 ; const char *format
| 0x08048499 8b4508 mov eax, dword [s] ; [0x8:4]=-1 ; 8
| 0x0804849c 890424 mov dword [esp], eax ; const char *s
| 0x0804849f e800ffffff call sym.imp.sscanf ; int sscanf(const char *s, const char *format, ...)
| 0x080484a4 8b45fc mov eax, dword [var_4h]
| 0x080484a7 83e001 and eax, 1
| 0x080484aa 85c0 test eax, eax
| ,=< 0x080484ac 7518 jne 0x80484c6
| | 0x080484ae c704246b8604. mov dword [esp], str.Password_OK ; [0x804866b:4]=0x73736150 ; "Password OK!\n" ; const char *format
| | 0x080484b5 e8dafeffff call sym.imp.printf ; int printf(const char *format)
| | 0x080484ba c70424000000. mov dword [esp], 0 ; int status
| | 0x080484c1 e8eefeffff call sym.imp.exit ; void exit(int status)
| | ; CODE XREF from sym.parell (0x80484ac)
| `-> 0x080484c6 c9 leave
\ 0x080484c7 c3 ret
此函数先通过sscanf函数将passcode转化为了整数,过程与前一个函数类似。
| 0x080484a4 8b45fc mov eax, dword [var_4h]
| 0x080484a7 83e001 and eax, 1
| 0x080484aa 85c0 test eax, eax
得到的整数进行and eax, 1
操作,相当于模2。
test eax, eax
进行判断。test的含义如下:
The test instruction sets the zero flag if the result of a bitwise logical AND operation with both operands equals 0. The result is zero if either one of the parameters, or both, equal zero. None of the provided operands are altered using this instruction.
来源:https://maxkersten.nl/binary-analysis-course/assembly-basics/crash-course/
即若两个操作数中至少一个为零,则zf被置为0.
| ,=< 0x080484ac 7518 jne 0x80484c6
| | 0x080484ae c704246b8604. mov dword [esp], str.Password_OK ; [0x804866b:4]=0x73736150 ; "Password OK!\n" ; const char *format
| | 0x080484b5 e8dafeffff call sym.imp.printf ; int printf(const char *format)
| | 0x080484ba c70424000000. mov dword [esp], 0 ; int status
| | 0x080484c1 e8eefeffff call sym.imp.exit ; void exit(int status)
| | ; CODE XREF from sym.parell (0x80484ac)
| `-> 0x080484c6 c9 leave
\ 0x080484c7 c3 ret
利用test的结果进行条件跳转,分析可知,若eax为0,则zf为0,等价于cmp的两个操作数相等,jne不跳转,顺序执行。而顺序执行将会打印成功的信息后退出,这是我们的目标。否则将会退出该函数,回顾前一个函数,我们知道这样一来继续运行就会给出密码错误的信息。从而可知我们需要让test的结果为将zf置为0,则eax需要为0,即输入的密码应当是偶数。
综上所述,密码应当是一个各位相加和为16的偶数。
Comment is disabled to avoid unwanted discussions from 'localhost:1313' on your Disqus account...