Crackme(目标程序):https://crackmes.one/static/crackme/5c268e8333c5d41e58e00654.zip
Ghidra(NSA反编译工具):https://github.com/NationalSecurityAgency/ghidra
BlackArch(操作系统):https://blackarch.org/
Pycharm(python编译器):https://www.jetbrains.com/pycharm/
一、使用Ghidra加载目标程序,跟踪反编译代码。
1、运行程序参数提示如下:
[ BlackArch ~ ]# ./keyGme
Usage: ./keyGme [serial-key]
2、在函数列表中搜索程序主函数“main”,找到该函数的代码如下:
undefined4 main(int param_1,undefined4 *param_2) { undefined4 uVar1; size_t sVar2; uint uVar3; if (param_1 == 2) { sVar2 = strlen((char *)param_2[1]); if (sVar2 == 0x10) { //serial-key的长度为十六进制的0x10,即十进制的16,注册码的长度为16个字符。
uVar3 = FUN_08048323((char *)param_2[1]); //函数FUN_08048323的返回值赋值给变量uVar3 if (uVar3 == 0) { puts(s_Sorry,_the_code_was_invalid._08049749); } else { puts(s_Congratulation,_You_are_register_08049721); //如果uVar3的值为1则提示注册成功 } uVar1 = 0; } else { puts(s_Your_key_must_be_16_characters._08049701); uVar1 = 2; } } else { printf(s_Usage:_%s_[serial-key]_080496e9,*param_2); uVar1 = 1; } return uVar1; }
3、双击函数名称“FUN_08048323”得到该函数的代码如下:
uint FUN_08048323(char *param_1)
{
size_t sVar1; //定义序列号中字符位置
int iVar2;
int local_10; //定义循环使用的计数器
int local_c; //定义序列号字符对应的ASCii码
sVar1 = strlen(param_1); //sVar1的初始值为1,结束值为16
local_c = 0;
local_10 = 0;
do {
if ((int)(sVar1 - 1) <= local_10) {
return (uint)(local_c == (int)param_1[sVar1 - 1]);
}
iVar2 = FUN_080482c1(param_1[local_10]);
if (iVar2 == 0) {
iVar2 = FUN_080482f2(param_1[local_10]);
if (iVar2 == 0) {
puts(s_Found_invalid_character!_080496d0); //如果FUN_080482c1和FUN_080482f2的返回值为0,则提示无效字符。
/* WARNING: Subroutine does not return */
exit(3); //跳出循环
}
}
local_c = (local_c + (int)param_1[local_10] >> 1) % 0xf00 + 10; //序列号中,最后一个字符的ascii值等于\
通过公式(character_sumasc+ord(previous_character))>>1%3840+10循环累加
local_10 = local_10 + 1; //计数器加1
} while( true );
}
4、双击函数名称“FUN_080482c1”和“FUN_080482f2”得到如下代码:可以判断出序列号中只包含大写字母A-Z和数字0-9中的随机字符
undefined4 FUN_080482c1(char param_1) { undefined4 uVar1; //定义函数返回值为uVar1 if ((param_1 < 'A') || ('Z' < param_1)) { //如果函数变量param_1不在A-Z这些字母中的话,返回值为0,即无效字符。 uVar1 = 0; //所以序列号中包含的字母范围为A-Z } else { uVar1 = 1; } return uVar1; } undefined4 FUN_080482f2(char param_1) { undefined4 uVar1; //定义函数返回值为uVar1 if ((param_1 < '0') || ('9' < param_1)) { //如果函数变量param_1不在0-9这些数字中的话,返回值为0,即无效字符。 uVar1 = 0; //所以序列号中包含的数字范围为0-9 } else { uVar1 = 1; } return uVar1; }
二、编写python脚本,生成序列号。
该脚本满足以下几个条件:
1、序列号长度为16位
2、序列号中包含的字母范围为A-Z
3、序列号中包含的数字范围为0-9
4、最后一个字符的ascii值是前15个的值通过公式character_sumasc=(character_sumasc+ord(previous_character))>>1%3840+10累加得来
#!/usr/bin/env python # -*- coding: utf-8 -*- import random import string choices = string.ascii_uppercase + string.digits def key_check(key): character_sumasc = 0 for k in key: character_sumasc += ord(k) character_sumasc = character_sumasc >> 1 character_sumasc %= int('0xf00',16) character_sumasc += 10 final_letter = chr(character_sumasc) if final_letter in choices: key += final_letter print("Key: {0}".format(key)) else: key_gen() def key_gen(): key = "" while len(key) < 15: key += random.choice(choices) key_check(key) if __name__ == '__main__': key_gen()
附:http://www.runoob.com/python/python-operators.html