banner
lca

lca

真正的不自由,是在自己的心中设下牢笼。

《从零开始学IDA逆向》学习笔记-18(编写注册机)

image

main 函数及其参数#

第 17 章中,通过远程调试的方式对程序进行了脱壳,此处实验的程序来自第 17 章脱壳后的程序。这里直接用作者给的程序,而不加载自己脱壳的程序。

image

此章对上述程序进行分析并编写注册机。

复习下程序,32 位架构。

image

打开 ida 加载脱壳后的程序,首先查看字符串。

image

此程序运行后,在命令行输出Pone un User,英文是Enter a user

单击Pone un User,跳转到程序具体位置。

image

按 “X” 键查看字符串引用

image

点击 ok,进入引用具体位置

image

上图中,在以 ebp 为基址的函数中,首先执行push ebp指令将上一层函数的 ebp 值保存到栈上,然后执行mov ebp,esp,将 ebp 作为计算函数本身的局部变量、参数、缓存区的基准。

image

之后再执行 sub esp,94h 指令从 ebp 的基址开始获取 0x94 个字节用于存储局部变量和缓存区

对函数任意变量或者参数双击,ida 会显示函数的静态栈视图,下图点击 var_4 的显示内容,静态栈视图

image

上图中,该函数有两个参数,也就是函数的默认参数 argc 和 argv,在函数调用之前通过 push 指令传到了栈上,并且显示在返回地址(r)的下方。(注:书中这里讲解一开始没有参数,后面将 sub_201070 重命名为 main 函数后才显示了这两个参数)

这两个参数虽然显示了,但是未被引用,所以也可以当成无参

image

回到静态栈视图,S 表示STO RED EBP,是通过push ebp指令保存的上一层函数的 ebp,再上方就是局部变量空间,一般都有一个var_4变量来保护栈,防止缓寸区溢出。

image

var_4有两处引用,第一处是函数的起点,用于在栈上存储一个安全口令security cookie

image

这是一个随机数,在函数刚运行时,跟 ebp 进行异或运算后,结果保存到var_4

image

上图中是另一处引用,var_4的值传给了 ecx,然后在与 ebp 进行异或运算,恢复原始值,然后再调用另一个函数检查其值

点击@__security_check_cookie@4,进入到这个函数内部,如下图

image

上图中,如果值一切正常,则返回,如果 ecx 中不是_security_cookie的值,将会终止进程,而不是执行返回,这只会发生在内存溢出将 var_4 覆盖的时候

按 N 键,现在将var_4重命名为COOKIE

image

下面的函数命名为check_cookie

image

修改后最终如下:

image

回到函数起始点处,有两个变量还不清楚作用,一个是var_90的初始值为0,另一个是size,初始值为8,如下图

image

如下图,查看var_7d的引用,在某个函数被调用后,var_7d的值被传入了 AL 寄存器,然后将值传给了 EDX 寄存器,检查其值是否为 0,确定输出 Good reverser 或者 Bad reverser,所以这是一个单字节的变量,重命名 SUCCESS_FLAG,也就是最终计算的 flag 值。

image

按 N 重命名对var_7d进行重命名

image

将左侧注册成功的代码块改成绿色,右侧注册失败改成黄色,上图中,只要修改 JZ 这个指令,也就能实现成功注册。

用户名和密码处理#

image

上图中,有另一个变量var_90,初始值为 0,程序将 BUF 中的每个字节读取出来,传入 edx(在 0x231109 指令处),并和var_90相加,第一次循环中加的是 0,结果再保存到var_90,那么之后的循环中 edx 加的是之前所有字节的和,将var_90重命名为SUMMARY。如下图

image

上图中var_84是循环的计数器,计数器加 1,只循环前 4 个字节,var_84大于等于 4 会跳出循环。将其命名为COUNTADDR

image

为了让这个循环更好的显示,按住 ctrl 键,在这三个循环上单击,右键选择 group nodes

image

最终显示如下

image

如果想取消编组,则右键选择 unhide group 或者点击图标

image

循环之后,获取密码

image

程序使用 Buf 存放获取的密码,因为程序已经保存了用户名前 4 个字节的和。

image

上图中,程序接着调用strlen()函数获取密码的长度,如果长度小于 4,那么就退出程序,如果不小于 4,那么程序进入右边绿色代码块。

image

右边绿色代码块中,使用atoi函数将密码字符串转成一个十六进制数,这个函数类似 python 中的hex()

image

然后十六进制密码和0x1234进行异或运算,然后保存到 edx 变量中。

image

上图中,将密码与0x1234异或的结果及用户名前 4 个字节的和传入sub_231010函数中进行比较,根据比较的结果决定输出结果。

image

sub_231010函数的参数中,arg_4 参数是首先传到栈上的,可以对这两个参数进行重命名。

image

右键单击set type,ida 会根据参数识别函数原型。

image

函数声明如下图所示

image

ida 自动注释与传入参数一致

image

sub_231010内部,在两个参数比较之前,密码变量传入了 eax,执行了 shl eax,1,相当于乘以 2

image

最后进行比较,如果这两个数相等,程序转向绿色代码并将 1 传入 al,跳出循环,然后传入SUCCESS_FLAG,最终决定是否注册成功。

image

算法总结#

程序首先将用户名的前 4 个字节相加
密码转换为 16 进制后和 0x1234 进行异或运算,结果再乘以 2

下面构建一个基于用户名的公式,注册机也是基于这一点,根据用户名计算密码

x = password (16进制数)
(x ^ 0x1234)*2 = SUCCESS_FLAG

那么x ^ 0x1234 = (SUCCESS_FLAG/2)

由于异或运算可逆。

A ^ B = C
A = B ^ C

那么x = (SUCCESS_FLAG/2) ^ 0x1234

使用 python 编写注册机#

假如用户输入 “pepe”,长度小于 8 字节,那么字节之和就可以按如下方式计算

sum = 0
user='pepe'
length=len(user)

for i in range(length):
	sum+=ord(user[i])

print(hex(sum))

那么 pepe 前 4 个字节之和就是:

sum = 0
user='pepe'

for i in range(4):
	sum+=ord(user[i])

print(hex(sum))

image

接下来编写一个适用于任何合法用户名的注册机

sum = 0
user=input("input user name:")

length=len(user)

for i in range(4):
	sum+=ord(user[i])

if(length>=4):
	print(hex(sum))

使用 input () 函数获取命令行输入,但是现在上述代码适用于任何合法的账户

根据之前总结的公式x = (SUCCESS_FLAG/2) ^ 0x1234,将用户名的计算结果除以 2,在和 0x1234 进行异或运算,找到十六进制的密码

sum = 0
user=input("input user name:")

length=len(user)

for i in range(4):
	sum+=ord(user[i])

print(user)

if(length>=4):
	print("success_flag",hex(sum))
	password = (sum//2)^0x1234
	print("password:",password)

image

目前注册机已经完成,密码也从 16 进制转成了 10 进制,python 的默认输出就是 10 进制

上述代码当输入用户名字符数达到 8 个字符时程序会崩溃,因为字符串最后还有一个终止符 null,连同终止符在内不能超过 8 个。当然输入 7 个字符是没问题的。

最后还有一个问题就是如果 4 个字符的和是个奇数。因为在比较之前密码是乘以 2 的,结果永远是一个偶数,所以这种情况就没有解。

加个检查流程

sum = 0
user=input("input user name:")

length=len(user)

for i in range(4):
	sum+=ord(user[i])

print(sum)
print(user)

if(sum%2==0):
	print("偶数")
	if(length>=4):
		print("success_flag",hex(sum))
		password = (sum//2)^0x1234
		print("password:",password)
else:
	print("奇数")

检查用户名字节加和 sum 除以 2 的余数,如果不等于 0,sum 是奇数,对应的密码是无解的。

奇数的情况

image

偶数的情况

image

这样,注册机就完成了

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.