【吾爱】逆向实战crackme160破解记录(二)

article/2025/6/8 9:59:36

前言

最近在拿吾爱上的crackme程序练练手,发现论坛上已经有pk8900总结好的160个crackme,非常方便,而且有很多厉害的前辈已经写好经验贴和方法了,我这里只是做一下自己练习的记录,欢迎讨论学习,感谢吾爱论坛的各位大神。

程序本身无病毒,杀毒报错可无视,如果实在担心也可也用虚拟机练习

由于cm160里面有不少是相同逻辑的程序,所以如果再遇到比较类似的,会精简一下或者直接跳过。

aLoNg3x.1

peid查一下是32位没有加密壳,得知编写语言是Delphi。

程序打开,发现是输出Nome和codice
在这里插入图片描述
点开about后发现了提示,把程序拖进darkde4里面看一下;
在这里插入图片描述
找到了okclicl和cancellaclick的两个按钮
根据dark给的地址,回到od里面去找到cancellaclick对应的地址0x442ea8。
在这里插入图片描述

输入111和222然后点击按钮,od里面逐步走一下看一下,发现下面的寄存器分别先读取了密码,然后是账户,再然后是一个je跳转,之后call了一个函数就直接返回了,所以猜测这个je是关键的判断,直接nop掉看一下
在这里插入图片描述
在这里插入图片描述
nop掉之后发现这个按钮消失了 ,然后是ok按钮从开始的不可选中变成了可以点击。
证明这个je确实是判断正误的关键跳转,这个je上面有三个被call的函数,用了od的SwissArmyKnife插件看一下函数名称。
在这里插入图片描述
前两个是getText函数,第三个是sub_442AF4函数
回到ida里面看一下:

在这里插入图片描述
分析一下逻辑:
a1,a2是传进来的账户和密码,这是一个验证账户胡序列号的函数。
首先检查用户名长度 v12(即 a1)是否 ≤5,若长度≤5,直接返回 0(验证失败)。
然后计算动态乘数 v2​​,通过用户名第5个字符(索引4)计算一个动态值。
取用户名第5字符的ASCII值,模7后加2,作为参数传递给 sub_442A20(点进去分析发现是个阶乘函数),得到v2。
遍历用户名所有字符,计算它的ascii码与v2的积的和 v3。计算 v3 - v11(v11 是输入的序列号 a2)
检查结果是否为 31337(十六进制0x7A69)。相等就返回1,表示成功;不相等返回0,表示失败。
所以验证的条件就是

S = (v2 * Σ username[i]) - 31337其中:
v2 = (username[4] % 7 + 2)!
Σ username[i] 是用户名所有字符的ASCII值之和。

扔给ai写个脚本:

import mathdef generate_serial(username):if len(username) <= 5:return "错误:用户名长度需>5字符"# 计算阶乘因子(第5字符ASCII%7+2)n = (ord(username[4]) % 7) + 2factor = math.factorial(n)  # 计算加权和total = 0for char in username:total += factor * ord(char)return total - 31337if __name__ == "__main__":username = input("请输入用户名(长度>5):")print(f"序列号:{generate_serial(username)}")

在这里插入图片描述
在这里插入图片描述
输入后可以发现右边按钮已经没了。
接着处理ok按键。
在这里插入图片描述
根据刚刚的经验,发现俩getText和一个没名字的函数还有一个je跳转。
猜测跟刚刚按钮的逻辑是一样的,下断点然后给je判断nop掉。
在这里插入图片描述
发现按钮消失了,我们的猜测是正确的,那么去这个sub_442ba0看看是啥逻辑。
在这里插入图片描述
int __usercall sub_442BA0@<eax>(int a1@<eax>, int a2@<edx>, int a3@<ebx>, int a4@<esi>)
这段代码实现了一个字符串验证逻辑。
它首先将输入整数转换为字符串,若字符串长度超过5则从末尾开始对每个字符进行变换:取字符ASCII值的平方乘以当前字符位置索引,再对25取模后加65(生成A-Z范围的大写字母)。
变换后的字符串与原始输入字符串进行严格比较,若完全匹配则返回验证成功标志1,否则返回0。

​​按字符位置从后向前处理​​(v6 从字符串长度递减至 1):
取原字符的 ASCII 值(*(unsigned __int8 *))
计算平方值(原字符²)
乘以当前位置索引(v6)
取模 25(% 25)
加 65(转换为大写字母 A-Z 的 ASCII 范围)
​​结果​​:将序列号整数的每个字符转换为特定大写字母

核心逻辑:

(v7 + v6 - 1) = v6 * (*((v16 + v6 - 1) * *(v16 + v6 - 1)) % 25 + 65;

写出脚本:

def generate_username(serial):user_name = [''] * len(serial)# 从最后一个字符向前处理(索引从高到低)for i in range(len(serial)-1, -1, -1):# 计算新字符: (serial[i]² × (i+1)) % 25 + 65char_val = ord(serial[i])new_char = chr((char_val * char_val * (i + 1)) % 25 + 65)user_name[i] = new_charreturn ''.join(user_name)def main():while True:serial = input("\n请输入序列号(输入0退出): ")if serial == "0":breakusername = generate_username(serial)print(f"生成用户名: {username}")if __name__ == "__main__":main()

在这里插入图片描述
现在写出来了两个脚本,试着去测试一下:
账号输入qwerty,脚本计算序列号是461143
在这里插入图片描述
在这里插入图片描述
然后用序列号反推字符串EHDEUG
在这里插入图片描述
在这里插入图片描述
好了,现在根据要求 已经让两个按钮消失了,得到了图片RingZ3ro

aLoNg3x.2

peid打开:
在这里插入图片描述
跟前面的cm一个作者,而且还是Delphi编写的。
点开程序:
在这里插入图片描述
点开about看过之后,发现要求依旧是让按钮消失,能够看到图片。
一样的思路,扔进dede里面看一下
在这里插入图片描述
发现了按钮的地址,然后扔进ida里面导出map文件,把程序和map文件扔进xdbg里面调试。
回到程序上,发现register挡着图片,cancella在侧面,先看一下register按钮。
所以找到地址0x442f28
在这里插入图片描述
下断点,然后逐步跑一下看一下。
发现一个字符串443038:"You MUST insert a valid Long Integer Value in the Code Editor... Thank you :)"这里应该是格式判断的函数。
所以接着往下看,又是一个提示44309C:"Please... The Code Must be > 0"序列号必须大于零。
根据动调逻辑,会发现0x442f640x442f9b这俩分别是俩格式判断,所以根据动调逻辑,0x442fc0这里跳过的部分代码可能就是 核心内容判断逻辑。nop一下看看内容。
在这里插入图片描述

在这里插入图片描述
按钮现在消失了,所以核心的判断逻辑就在这里。跳转前被call的函数是sub_4429A8这里应该就是第一个按钮的验证函数。
在这里插入图片描述
回到上一层看看传入的a1,a2,a3是啥。
if ( (unsigned __int8)sub_4429A8(dword_445830, v4, (int)v9) )
dword_445830应该是个固定参数。
v4是一个Int数字,v9是一个指针,那应该指的是字符串nome。
现在去看一下函数的逻辑:

# 伪代码描述验证条件
if (abs(calc_result) % 666666 == (codice % 80 + codice // 89 + 1):return True

但是这个验证里面是需要获得dword_445830的值,回到点击按钮的函数里面,看一下这个参数在哪里会被赋值。
在这里插入图片描述

发现在30行这个分支(根据逻辑猜测是判断序列号是负数),下面参数变成0了,在23行分支这里,参数被赋值了。根据逻辑赋值为0是错误的。所以要看如何进入到23行的逻辑里面,想起来在od里面正确进入验证前有两个格式判断,先判断是整型数据,在判断是正数,跟这里的逻辑是一样的,所以可以知道23行的判断是要求序列号不是整型数据,然后参数dword_445830才能被赋值,赋值是用dword_445830 = Libmain::TWindowDesigner::SelectAll(v9);来进行的。

下面的方法有两种,要么我们进到 Libmain::TWindowDesigner::SelectAll(v9)里面去看静态逻辑,v9这里是用户名的指针,所以跟用户名有关系,要么我们指定一个用户名,回到xdbg里面动调一下看一下dword_445830的值。
进到Ida里面看了一下,
在这里插入图片描述
这个this是nome的字符串指针。
把逻辑扔给ai转成python代码跑一下

def calculate_serial(username: str) -> int:# 检查长度if len(username) <= 5:return 0# 初始化累加器(对应v1)result = 891# 遍历所有相邻字符对(长度-1次循环)for i in range(len(username) - 1):current_char = username[i]          # 当前字符(对应v3-1)next_char = username[i + 1]          # 下一个字符(对应v3)# 核心计算公式:# result += (当前字符ASCII) * ((下一字符ASCII % 17) + 1)result += ord(current_char) * ((ord(next_char) % 17) + 1)# 最终处理:取绝对值后模29000return abs(result % 29000)# 交互式脚本
if __name__ == "__main__":while True:try:# 获取用户输入username = input("请输入字符串: ").strip()# 计算并输出结果serial = calculate_serial(username)print(f"\n计算结果:{serial}")print("-" * 40)# 询问是否继续cont = input("是否继续计算? (y/n): ").lower()if cont != 'y':print("程序已退出")breakexcept Exception as e:print(f"发生错误: {e}")

在这里插入图片描述

这里指定字符串是NULLPTR,得到参数是6026。
写出sub_4429A8的逆向逻辑:

def generate_serial(account: str, a1: int = 6026) -> int:n = len(account)if n <= 4:raise ValueError("账户长度必须大于4个字符")total = 0for i in range(n):        for j in range(n-1, -1, -1):  total += a1 * ord(account[i]) * ord(account[j])v = abs(total) % 666666   if v == 0:return 0  x = (v - 1) * 89return x + (80 - x % 80) if __name__ == "__main__":try:account = input("请输入账户名称(长度>4): ")serial = generate_serial(account)print(f"账户 '{account}' 的有效序列号为: {serial}")except ValueError as e:print(f"错误: {e}")

在这里插入图片描述

用户名设置成12345678,得到序列号13682800,回到程序去验证下。但是这里需要先让程序给参数赋值,所以输入流程是序列号先输入字母NULLPTR,确认后再输入正确的序列号。发现成功了,但是按钮消失后又出现了一个Agian的按钮,说是要再来一次,nome框不让修改了,那就按照刚刚的逻辑再来一次,顺带把上面两个代码合并成一个脚本。

def calculate_dword(username: str) -> int:# 检查长度if len(username) <= 5:return 0# 初始化累加器(对应v1)result = 891# 遍历所有相邻字符对(长度-1次循环)for i in range(len(username) - 1):current_char = username[i]          # 当前字符(对应v3-1)next_char = username[i + 1]          # 下一个字符(对应v3)# 核心计算公式:# result += (当前字符ASCII) * ((下一字符ASCII % 17) + 1)result += ord(current_char) * ((ord(next_char) % 17) + 1)# 最终处理:取绝对值后模29000return abs(result % 29000)def generate_serial(account: str, a1) -> int:n = len(account)if n <= 4:raise ValueError("账户长度必须大于4个字符")total = 0for i in range(n):        for j in range(n-1, -1, -1):  total += a1 * ord(account[i]) * ord(account[j])v = abs(total) % 666666   if v == 0:return 0  x = (v - 1) * 89return x + (80 - x % 80) if __name__ == "__main__":try:account = input("请输入账户名称(长度>4): ")dword_445830 = input("请输入序列号的字符串: ")serial = generate_serial(account,calculate_dword(dword_445830))print(f"账户 '{account}' 的有效序列号1为: {serial}")dword_445830 = input("请输入Agian序列号的字符串: ")serial = generate_serial(account,calculate_dword(dword_445830))print(f"账户 '{account}' 的有效序列号2为: {serial}")except ValueError as e:print(f"错误: {e}")

结合之后的脚本,就重新改下用户名字改一下,用户名就用admin,第一次的字符串用asdfgh,第二次字符串用qwerty。(这里发现如果字符串是大小写字母混合,脚本计算的结果是错误的,不知道是不是逆向的时候少了某个限制条件,但是纯小写字母或者纯大写字母就能过,就不再回头检查了)

在这里插入图片描述
脚本跑一下,然后输入:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
现在就完成了cm的完整破解。

简要总结

原本想再写几个cm的,但是这两个代码量也不小,写完感觉博客有点长了,那就这两个单独一篇吧,而且还是同一个作者aLoNg3x的。程序的逻辑设计的都很好,还挺适合练习的。


http://www.hkcw.cn/article/IPEyqwVkWJ.shtml

相关文章

C# Onnx 动漫人物人脸检测

目录 效果 模型信息 项目 代码 下载 参考 效果 模型信息 Model Properties ------------------------- stride&#xff1a;32 names&#xff1a;{0: face} --------------------------------------------------------------- Inputs ------------------------- name&am…

使用cmd命令行创建数据库和表-简单步骤记录

前提&#xff1a; 已安装MySQL 步骤&#xff1a; 1.WinR&#xff0c;回车&#xff0c;输入cmd&#xff0c;回车 2.输入 mysql -u root -p 后&#xff0c;输入自己的密码&#xff0c;看到welcome等字样就是成功登录了MySQL 3.创建数据库 create database success; &#xff0…

Centos7使用rpm升级glibc2.28

Centos7使用rpm升级glibc2.28 检查glibc版本下载glibc2.28的rpm包使用rpm包升级到glibc-2.28结果验证 检查glibc版本 ldd --version下载glibc2.28的rpm包 参考&#xff1a; https://www.cnblogs.com/caya-yuan/p/10561439.html 下载 glibc、make 的 feroda29(fc29)系统 rpm包…

堆叠弹窗 VS 队列弹窗之争

前言 如果一个页面上有多个弹窗&#xff0c;设计上是把前一个弹窗暂时隐藏还是盖住前一个弹窗多一点&#xff1f; 在多弹窗设计的情境下&#xff0c;最佳实践通常倾向于以下两种处理方式&#xff1a; 1、堆叠弹窗 新弹窗覆盖旧弹窗&#xff0c;但每个弹窗保持完整显示&#…

刷leetcode hot100返航必胜版--链表6/3

链表初始知识 链表种类&#xff1a;单链表&#xff0c;双链表&#xff0c;循环链表 链表初始化 struct ListNode{ int val; ListNode* next; ListNode(int x): val&#xff08;x&#xff09;,next(nullptr) {} }; //初始化 ListNode* head new ListNode(5); 删除节点、添加…

[概率论基本概念4]什么是无偏估计

关键词&#xff1a;Unbiased Estimation 一、说明 对于无偏和有偏估计&#xff0c;需要了解其叙事背景&#xff0c;是指整体和抽样的关系&#xff0c;也就是说整体的叙事是从理论角度的&#xff0c;而估计器原理是从实践角度说事&#xff1b;为了表明概率理论&#xff08;不可…

React-native之Flexbox

本文总结: 我们学到了 React Native 的 Flexbox 布局&#xff0c;它让写样式变得更方便啦&#xff01;&#x1f60a; Flexbox 就像一个有弹性的盒子&#xff0c;有主轴和交叉轴&#xff08;行或列&#xff09;。 在 RN 里写样式要用 StyleSheet.create 对象&#xff0c;属性名…

学习日记-day21-6.3

完成目标&#xff1a; 目录 知识点&#xff1a; 1.集合_哈希表存储过程说明 2.集合_哈希表源码查看 3.集合_哈希表无索引&哈希表有序无序详解 4.集合_TreeSet和TreeMap 5.集合_Hashtable和Vector&Vector源码分析 6.集合_Properties属性集 7.集合_集合嵌套 8.…

ABP-Book Store Application中文讲解 - Part 6: Authors: Domain Layer

ABP-Book Store Application中文讲解 - Part 6: Authors: Domain Layer 1. 汇总 ABP-Book Store Application中文讲解-汇总-CSDN博客 2. 前一章 ABP-Book Store Application中文讲解 - Part 5: Authorization-CSDN博客 项目之间的引用关系。 ​ BookAppService利用的是Cu…

智慧高铁站:数字时代交通枢纽的标杆

智慧高铁站作为现代综合交通体系的核心节点&#xff0c;通过数字技术与基础设施的深度融合&#xff0c;正在重塑旅客出行体验与车站运营模式。这一转型不仅体现在技术应用层面&#xff0c;更代表着交通服务理念的根本性变革&#xff0c;为现代交通枢纽建设树立了全新标杆。 一、…

ARM架构推理Stable Diffusiond

代码仓库&#xff1a; https://github.com/siutin/stable-diffusion-webui-docker.git Docker容器地址&#xff1a; https://hub.docker.com/r/siutin/stable-diffusion-webui-docker/tags git clone https://github.com/siutin/stable-diffusion-webui-docker.git cd stabl…

关于 KWDB 数据存储的几件事儿

邻近粽子节&#xff0c;KWDB 的朋友给我发消息&#xff0c;问我吃过红茶味的粽子没&#xff0c;作为北方人的我一般只吃蜜枣白粽&#xff0c;还没见过茶香粽子&#xff0c;顶多泡碗祁红&#xff0c;就着茶水吃粽子。 她又问道&#xff0c;两个月时间到了&#xff0c;你准备好了…

酵母杂交那些事儿(一)

酵母单杂、酵母双杂、酵母三杂&#xff0c;仅仅一个字的区别&#xff0c;你对它们了解吗&#xff1f;这些经常用到的实验&#xff0c;它们的原理你确定都搞清楚了吗&#xff1f;如果没有&#xff0c;那么今天你就来对地方了&#xff0c;因为伯远生物&#xff08;https://plant.…

sqlite3 命令行工具详细介绍

一、启动与退出 启动数据库连接 sqlite3 [database_file] # 打开/创建数据库文件&#xff08;如 test.db&#xff09; sqlite3 # 启动临时内存数据库 (:memory:) sqlite3 :memory: # 显式启动内存数据库文件不存在时自动创建不指定文件名则使用临时内…

项目开发:【悟空博客】基于SSM框架的博客平台

目录 一.导入 1.Spirng框架 2.SpirngMVC 二.项目介绍 &#xff08;一&#xff09;项目功能 &#xff08;二&#xff09;页面展示 1.注册页面 2.登录页面 3.列表页面 4.详情页面 5.编辑页面 三.准备工作 1.用户表——userinfo 2.文章表——articleinfo 3.插入数…

大话软工笔记—分离之组织和物品

一. 组织 组织在架构中既不属于“业务架构”&#xff0c;也不属于“管理架构”&#xff0c;它是由组织结构、角色、权限等要素构成。 1. 组织的概念 组织&#xff08;名词&#xff09;&#xff0c;将资源按照某个目标构建出一个有层次的集合体&#xff0c;即组织结构。 组织…

伊吖学C笔记(5、数组、表达式、考题设计)

一、数组 数组是由同一种类数据构成的集合。就好比一个班所有同学的身高&#xff0c;一个月的日平均气温&#xff0c;抽样调查的一百个数据...等等&#xff0c;都可以当作一个数组。构建数组是为了对同类的多个数据实行高效管理。 1.数组定义 格式&#xff1a;类型说明 数组…

由docker引入架构简单展开说说技术栈学习之路

想象一下&#xff0c;你开了一家线上小卖部&#xff08;单机版&#xff09;&#xff0c;突然爆单了怎么办&#xff1f;别急&#xff0c;技术架构的升级打怪之路&#xff0c;可比哆啦A梦的口袋还神奇&#xff01; 第1关&#xff1a;单枪匹马的创业初期&#xff08;单机架构&…

Dify知识库下载小程序

一、Dify配置 1.查看或创建知识库的API 二、下载程序配置 1. 安装依赖resquirements.txt ######requirements.txt##### flask2.3.3 psycopg2-binary2.9.9 requests2.31.0 python-dotenv1.0.0#####安装依赖 pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.…

Neovim - 打造一款属于自己的编辑器(一)

前言&#xff08;劝退&#xff09; neovim 是一个现代化&#xff0c;高扩展的 vim 编辑器 fork 版本&#xff0c;适合程序员打造极致高效的开发环境。 在正式开始 neovim 配置之前&#xff0c;我还是要劝退一下的。 很多人说使用 neovim 的都是变成高手&#xff0c;但我认为…