Hi铁布衫-CM Ver 0.001 WriteUp

本文作者:XDbgPYG(小吧唧)

发布时间:2023年8月28日

内容概要:Hi铁布衫-CM Ver 0.001 WriteUp

收集信息

有一段时间没写过代码了,源码早就忘光了,今天碰巧有时间,好好测试一下我的网络验证的强度。

PE 信息如下:

vc 8.0 above 是个很新的程序~

内存布局如下:

嗯,很帅。

字符串如下:

有一堆的 PYG 水印。(除了水印已无太多信息)

readMe:

  1. readMe 有提到程序是由 hi-Easyx 编写的。
  2. readMe 有提到程序由 登录窗口=> 功能窗口 组成。
  3. readMe 有提到这是一个 网络验证 CrackME

所以我们可以尝试通过 CreateWindow系列函数hi-Easyx相关字符串 进行定位 hi-Easyx 创建窗口的 Call 或 Sig(特征码)。

所以我们可以尝试通过 sendrecv 等网络函数进行回溯=>分析验证的加密算法、解密算法等对于网络验证来说很重要的东西。

调试

创建窗口函数的分析-失败

可以看到有个 Error creating window: 字符串,转到目标地址,进行控制流分析。

可以看出程序是通过 CreateWindowExW 创建的窗口,而在其地址上方有个 可能的关键信息:wchar_t *HiEasyX::g_lpszClassName

我习惯先对字符串进行搜索引用(函数的调用可能会有很多),经过引用搜索,发现俩个字符串都位于该函数中。

void __cdecl HiEasyX::InitWindow(...)

对这个函数进行引用分析,发现只有 1 个引用。

0x0007FF7169B8D4A lea rcx, ds:[0x0x0007FF7169B7C80]

继续回溯,看样子想通过 className 进行定位 创建窗口 函数的想法是泡汤了。

0x0007FF7169B8FA2 call HWND__* __cdecl HiEasyX::initgraph_win32(...)

累了,不跟踪了。

通过 send recv 定位 1 个关键点范围

如果程序的客户端验证没有使用到线程池技术(不考虑有保护的情况)的话,那么有趣的事情就会这样发生。

有线程池情况的 处理包流程图:没学原理,到时候给验证添加线程池后再来补好了。

无线程池(有循环)获取一段数据的情况:

无线程池(无循环)获取一段数据的情况:

现在搜索一下 send 的引用,发现许多调用,看样子程序是把 发包函数inline 了。

现在搜索一下 recv 的引用,看样子程序是把 收包函数inline 了。

不知道 收包函数、发包函数 是否有多种加密解密情况,如果有的话,就不逆向算法了。

有趣的地方来了,我似乎没有对缓冲区进行清理导致直接获得了一个信息 web|notice 。很明显,程序这是在获取公告。

既然如此,就碰碰运气,对所有 recv 函数的引用地址下断点,重启下程序,进行简单的测试。

获取公告
0x0007FF7169C1A2B | FF15 5FFB0600 | call qword ptr ds:[<&recv>] | 循环获取公告(看控制流有多次调用、应该是循环获取公告)
0x0007FF7169C1D13 | FF15 77F80600 | call qword ptr ds:[<&recv>] | 登录
login|pygChina|UserPassWord|superPassWord
0x0007FF7169C4C9F | FF15 EBC80600 | call qword ptr ds:[<&recv>] |

定位到 获取登录返回值 的地址了,对 recvbuffer 下硬件访问断点。

看样子程序是调用了 rc4 加密。
0x0007FF7169B9BF0 CryptoLibEx::RC4::Encode(unsigned char *, unsigned char *, int) 0x0007FF7169B9CCA | 48:8B05 575D0C00 | mov rax,qword ptr ds:[<class CryptoLibEx::RC4 rc4>] | 0x0007FF716A7FA28:&"1234567890..." 0x0007FF7169B9D8D | 41:3203 | xor al,byte ptr ds:[r11] | 看样子程序是调用了 rc4 解密。
0x0007FF7169B9DE0 public: class CryptoLib::stringx __cdecl CryptoLibEx::RC4::Decode(unsigned char *, int)

既然现在找到了验证的包加密、解密算法。那山寨一个出来就不是难题了。

CryptoLibEx::RC4::Decode 搜索引用下断点,并进行跟踪调试。

登录返回值 L"bad..."。
0x0007FF7169C4CB3 | E8 2851FFFF | call <hi铁布衫-cm ver 0.001.public: class CryptoLib::stringx __cdecl | 将 L"bad..." 转换为 "bad...",即 unicode2Asccii()。
0x0007FF7169C4CC4 | E8 A7B10x000 | call <hi铁布衫-cm ver 0.001.public: class std::basic_string<char, struc | 对字符串下硬件访问断点。可以看到如下关键信息。
0x0007FF7169C4DF7 | 4C:8B8424 00070000 | mov r8,qword ptr ss:[rsp + 0x700] |
0x0007FF7169C4DFF | 4C:3BC0 | cmp r8,rax |
0x0007FF7169C4E02 | 0F85 051D0000 | jne hi铁布衫-cm ver 0.001.7FF7169C6B0D |
0x0007FF7169C4E08 | 48:8D9424 8C030000 | lea rdx,qword ptr ss:[rsp + 0x38C] |
0x0007FF7169C4E10 | E8 6BFE0300 | call <hi铁布衫-cm ver 0.001.memcmp> |
0x0007FF7169C4E15 | 85C0 | test eax,eax |
0x0007FF7169C4E17 | 0F85 F01C0000 | jne hi铁布衫-cm ver 0.001.7FF7169C6B0D |
0x0007FF7169C4E1D | C605 32AA0B00 01 | mov byte ptr ds:[<bool Logined>],0x1 | TestClient.cpp:486
0x0007FF7169C4E2F | 48:8D0D 62710B00 | lea rcx,qword ptr ds:[<class HiEasyX::Window methodWindow>] | rcx:"bad..."

ps: 值得注意的是,我看到了创建窗口的符号,这说明第一个思路是对的,可能是因为操作失误了,导致没有追根到底。

看样子我们找到关键的控制流跳转了(jcc)。简单的修改下标志位和跳转,测试下:程序爆破成功(MayBeSuccess?)。

ps:在看完源代码后,发现这个窗口标题是服务器已经发现在破解情况下触发的;同时这个窗口是暗装窗口。

搜索下创建窗口的引用,有趣的来了,有三个窗口。这说明有 1 个是假的功能窗口。

现在,我不想继续跟下去了,要是分析了半天暗装算法,就得不偿失了。

通过 Server 端,拿出来一个 测试账号 玩玩。

uN: pygxDbg
pW: oKVxXlGKtomCUCML

正版卡记录控制流与数据

0x0007FF7169C4E17 jump
successResult: "bad2..."
0x0007FF7169C6B74 noJump
0x0007FF7169C6B7A | C605 D58C0B00 01 | mov byte ptr ds:[<bool Logined>],0x1 | TestClient.cpp:532

有趣的是,程序依旧被检测到调试了。(我也不知道为啥,也许是登录超时了)

嗯,下次要快点,重来一次,记录好数据。(控制流已经不用管了)

0x0007FF7169C6E36 | 48:8BC8                 | mov rcx,rax                          | rax:&L"PVZ Plug Ver 4.7\n"
0x0007FF7169C7090 | 48:8BC8 | mov rcx,rax | rax:L"46"
0x0007FF7169C72AE | 48:8BC8 | mov rcx,rax | rax:L"47"
0x0007FF7169C74C7 | 48:8BC8 | mov rcx,rax | rax:L"48"
0x0007FF7169C76AF | 48:8BC8 | mov rcx,rax | rax:L"50"
0x0007FF7169C7907 | 48:8BC8 | mov rcx,rax | rax:&L"MayBeSuccess?\n"

对比下窗口,看样子有 4 个数据是与本地算法有关的。(另外俩个分别是标题、标签的字符串,我记得是没有 数据复用 的情况的)

ps:通过看源代码,最后一个数据是通过这 4 个 整数 算出来的。如果有 1 个补错了,服务端就会检测到并警告。

刚才的是数据是窗口加载时要用到的(这又是一个不错的暗装点\检测点),还有一个 Check 会用到数据。

看样子是从 Server 上获取用户名,如果和本地用户名不匹配,估计又要扰一会头了。
0x0007FF7169C22BD | 48:8BC8 | mov rcx,rax | rax:&L"chinapyg"

好了,控制流和数据都拿到了,该逆向本地验证了。(笑

网络验证与本地验证相结合

在搞定网络验证后,还需要填写一个本地算法生成出来的 Serial 方可通过。

网络传输过来的数据还是服务于本地的软件,目前我的项目是这样,所以补了几百或几千个数据,收包解密后的数据依旧是加密的。

本地验证部分参考的这个 Web Site

山寨 Hi-铁布衫

我们拿到了正版数据,最简单的 Patch 方式,直接把记录到的 地址 给 Hook 掉,把数据补进去。不过为了贴近一丢丢的实际情况(这个方法太容易被对抗了),用下面的方法。

在知道了加密与解密的算法,在程序是单线程发包的情况下,可以尝试 Hook Rc4.EnCodeRc4.DeCode 把正版数据补回去。

分析 Hook 点

这台电脑没有环境,改天让周清帮忙装下~

所以,山寨就略了。

逆向 Rc4.EncodeRc4.DeCode

照着源码跟就好了~ (奇怪,我似乎找不到 CryptoLibEx)坏了,坏了,改日再补。

// main
rc4.SetNormalKey(); __forceinline int Send(std::string _Str)
{
UCHAR* en = new UCHAR[_Str.size()]{ 0 };
rc4.Encode((UCHAR*)_Str.c_str(), en, _Str.size());
return send(connection, (char*)en, _Str.size(), 0);
delete en;
} __forceinline std::string Recv()
{
auto _buffer = new char[1024]{ 0 };
int _bufferSize = 1024;
int size = recv(connection, _buffer, _bufferSize, 0);
auto r = rc4.Decode((UCHAR*)_buffer, size).GetString();
delete _buffer;
return r;
}

一不小心,发现没搭建好环境,尴尬。不过相信身为 Reverser/Ctfer/Geeker 不会因为这点困难而停止逆向的脚步~

总结、反思与铁布衫0.2

铁布衫 0.001 还是太脆弱了。

  1. 可以尝试通过 recv 的调用频率、引用定位关键点
  2. 网络验证无正版 KEY 不可破同时难以分析
  3. 发包与收包的缓冲区应该及时清除
  4. 有用的符号太多=>想办法把程序的符号信息全部去除或修改
  5. 验证协议过于简单=>使用成体系的协议算法=>逆向或收集其他网络验证协议以加强自身
  6. 创建窗口搜索引用失败的原因(还未尝试重现)
  7. 暗装可以设计多个
  8. 可以尝试实现动态心跳包来检测调试
  9. 实现多线程与服务端通信

链接

(2023.7.24)软件加密与解密-2-1-程序分析方法[XDbg].md

(2023.8.28)Hi铁布衫-CM Ver 0.001 - Cracked-writeup的更多相关文章

  1. Attribute meta-data#android.support.VERSION@value value=(25.4.0) from AndroidManifest.xml:25:13-35 is also present at AndroidManifest.xml:28:13-35 value=(26.1.0).

    Android Studio 编译项目的时候报错 Merging Errors: Error: Attribute meta-data#android.support.VERSION@value va ...

  2. Netsparker破解版5.3 Netsparker Enterprise 5.3.0.24388[cracked]

    Netsparker破解版5.3 Netsparker Enterprise 5.3.0.24388[cracked]该版本更新时间为2019年7月8日下载地址:1 https://www.dr-fa ...

  3. Visual Studio Code预览版Ver 0.3.0试用体验

    当你开始阅读这篇文章时,请先不要把Visual Studio Code和.net.Windows联想到一起,因为VS Code是一个跨平台,支持30多种语言的开箱代码编辑器.不管你是.Net.Java ...

  4. 多校5-MZL's Border 分类: 比赛 2015-08-05 21:28 7人阅读 评论(0) 收藏

    MZL's Border Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total ...

  5. Borg Maze 分类: POJ 2015-07-27 15:28 5人阅读 评论(0) 收藏

    Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9971   Accepted: 3347 Description The B ...

  6. Counterfeit Dollar 分类: POJ 2015-06-12 15:28 19人阅读 评论(0) 收藏

    Counterfeit Dollar Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 41559   Accepted: 13 ...

  7. Segment Tree with Lazy 分类: ACM TYPE 2014-08-29 11:28 134人阅读 评论(0) 收藏

    #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; stru ...

  8. Hdu 1507 Uncle Tom's Inherited Land* 分类: Brush Mode 2014-07-30 09:28 112人阅读 评论(0) 收藏

    Uncle Tom's Inherited Land* Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (J ...

  9. nginx 安装手记 分类: Nginx 服务器搭建 2015-07-14 14:28 15人阅读 评论(0) 收藏

    Nginx需要依赖下面3个包 gzip 模块需要 zlib 库 ( 下载: http://www.zlib.net/ ) zlib-1.2.8.tar.gz rewrite 模块需要 pcre 库 ( ...

  10. opnet的sink模块学习 分类: opnet 2014-05-18 10:28 161人阅读 评论(0) 收藏

    Sink模块的状态机很简单,只有INIT和DISCARD两个,非强制状态只有DISCARD用于包的销毁.Sink模块的作用就是销毁从输入流接收到的包,并且返回关于包的一系列统计量. Init的入口代码 ...

随机推荐

  1. LOTO示波器如何测试阻抗的频响曲线

    LOTO示波器如何测试阻抗的频响曲线 模块的输入输出端口,在电路分析上,一般简单表征为电阻来进行计算和分析.但多数情况下,这些端口并不是纯电阻的特性,更精确一些,它可能是电阻电容以及电感的组合,表现为 ...

  2. jmeter如何保存变量到结果jtl文件里

    将变量保存到结果jtl文件里,可以方便的在generate报告时,自行取用jtl中的变量进行展示,实现过程如下: 1.打开jmeter/bin目录下的jmeter.properties文件,将变量名加 ...

  3. AcWing 423. 采药

    辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师. 为此,他想拜附近最有威望的医师为师. 医师为了判断他的资质,给他出了一个难题. 医师把他带到一个到处都是草药的山洞里对他说:"孩子 ...

  4. 2019年蓝桥杯C/C++大学B组省赛真题(迷宫)

    题目描述: 下图给出了一个迷宫的平面图,其中标记为1 的为障碍,标记为0 的为可 以通行的地方. 010000 000100 001001 110000 迷宫的入口为左上角,出口为右下角,在迷宫中,只 ...

  5. MassTransit实现Saga模式概览

    原文地址:Saga Overview 编排一系列事件的能力是一个强大的功能,而MassTransit使这成为可能. saga是由协调器管理的长期事务.saga是由事件发起的,saga编排事件,saga ...

  6. odoo开发教程十二:web controlle

    一:路由 odoo.http.route(route=None, **kw) 装饰器可以将对应方法装饰为处理对应的http请求,该方法须是Controller的子类. route -- 字符串或数组, ...

  7. 如何使用Go中的Weighted实现资源管理

    1. 简介 本文将介绍 Go 语言中的 Weighted 并发原语,包括 Weighted 的基本使用方法.实现原理.使用注意事项等内容.能够更好地理解和应用 Weighted 来实现资源的管理,从而 ...

  8. 洛谷 P9047 [PA2021] Poborcy podatkowi

    题意 给一棵有边权的树,从中选出若干条长度为 4 的路径,要求边不交,求最大权值和. 数据范围:\(1\le n\le 2\times 10^5, -10^9\le w\le 10^9\). 题解 考 ...

  9. 为什么要重写equals要重写hashcode方法

    Java 比较(==, equals) 一.= = ==:比较两个对象的引用是否是同一个地址 二.equals object中equals方法调用的就是==,可以在其他类中重写该方法. 三.为什么要重 ...

  10. ClickHouse数据表迁移实战之-remote方式

    1 引言 ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS).我们内部很多的报表.数据看板都基于它进行开发.今天为大家带来remote方式的ClickHouse数据表迁 ...