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...

Theme Name