本题来自picoCTF2018
题目描述
Can you generate a valid product key for the validation program in /problems/keygen-me-1_3_a2370158b7b72b3863212502340f2c32
解题思路
首先用file查一下文件信息,32位elf可执行文件。
放到虚拟机里面,加上可执行权限,运行一下。
可以看到要求输入16个字符的key。
用IDA查看一下伪代码,可以直接在左边的函数窗口找到main
函数,或者通过字符串的交叉引用
找到main
函数,然后F5
查看伪代码.
可以看到有两个关键的函数,check_valid_key
和validate_key
,这两个函数对你的输入进行了检测。
我们首先来看check_valid_key
函数。
里面有一个check_valid_char
函数进行检查每一个字符,每一个字符的ascii要求在指定范围内,翻译过来就是由数字和大写字母组成。
check_valid_key
的功能出来了,就是验证你的输入是否合法(是否由数字和大写字母组成)。
validate_key函数如下图,也比较简单,v2是一个s的前15位进行运算然后累加的和,最后比较v2%0x24
与ord(s[15])
的值是否相等。注意有一个ord函数,这个函数不是python中的ord函数,是他自己定义的一个函数。
ord函数也比较简单,如果a1是数字(也就是ascii码在48到57之间)就返回a1-48
,否则返回a1-55
,字符串在C中都是以ascii码的形式存储的,所以能够与数字直接进行运算。
由于这个逻辑比较简单,我们可以手动算一个key,比如,前15位
我都让他为数字0
,那么ord(s[i])
返回到值为0
,v2
的计算方式如下面的代码。
for(int i=0; i<15; i++){
v2 += 1 * (i + 1)
}
v2 = 1+2+3+4+5+6+7+8+9+10+11+12+13+14+15 = 120
,然后模除以0x24
等于
12
,然后找一个大写字母,让ord(s[15])
的值等于12
就行,逆着推一下,12+55=67
,大写字母C
。最后构造出来的key为000000000000000C
。
输入正确的key,提示成功,但是没有flag,因为需要通过ssh连接上题目的服务器,在服务器上运行才能读取flag文件。
解题脚本
最后补充一个脚本,通过Z3来解这道题,大家有兴趣的话可以看一下。
# -*- coding: utf-8 -*
from z3 import *
a1 = [Int('a1_%d' % i) for i in range(16)]
s = Solver()
ord = lambda x: If(And(x > 47, x <= 57), x - 48, x - 55)
for i in range(16):
s.add(Or(And(a1[i] > 0x2f, a1[i] <= 0x39), And(a1[i] > 0x40, a1[i] <= 0x5a)))
v2 = Sum([(ord(a1[i]) + 1) * (i + 1) for i in range(15)])
v2 %= 0x24
print(v2)
s.add(v2 == ord(a1[15]))
if s.check() == sat:
m = s.model()
flag = ''
for i in range(16):
flag += chr(m[a1[i]].as_long())
print(flag)