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. 8张图带你全面了解kafka的核心机制

    前言 kafka是目前企业中很常用的消息队列产品,可以用于削峰.解耦.异步通信.特别是在大数据领域中应用尤为广泛,主要得益于它的高吞吐量.低延迟,在我们公司的解决方案中也有用到.既然kafka在企业中 ...

  2. 文心一言 VS 讯飞星火 VS chatgpt (27)-- 算法导论5.1 2题

    二.请描述 RANDOM(a,b)过程的一种实现,它只调用 RANDOM(0,1).作为a和b的函数,你的过程的期望运行时间是多少? 文心一言: 假设我们有一个伪随机数生成器,可以生成在[0,1)范围 ...

  3. C++面试八股文:什么是左值,什么是右值?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第16面: 面试官:什么是左值,什么是右值? 二师兄:简单来说,左值就是可以使用&符号取地址的值,而右值一般不可以使用&符号取地址. ...

  4. @SafeVarargs注解的使用

    在声明具有模糊类型(比如:泛型)的可变参数的构造函数或方法时,Java编译器会报unchecked警告.鉴于这些情况,如果程序员断定声明的构造函数和方法的主体不会对其varargs参数执行潜在的不安全 ...

  5. Spark架构与运行流程

    1. 阐述Hadoop生态系统中,HDFS, MapReduce, Yarn, Hbase及Spark的相互关系. 2. Spark已打造出结构一体化.功能多样化的大数据生态系统,请简述Spark生态 ...

  6. Codeforces Round #879 (Div. 2) A-E

    比赛链接 A 代码 #include <bits/stdc++.h> using namespace std; using ll = long long; bool solve() { i ...

  7. 统信UOS国产服务器操作系统(UOS Server 20-1060e)安装使用体验

    总体来说,UOS系统的安装还是很简明的.需要注意的是后期的驱动安装和其他各方面的使用细节. 以下是具体安装过程:(感谢统信软件河北团队的大力支持.) 特别感谢统信的郭赞.喵喵喵.Zero等各位大神的帮 ...

  8. Spring Loaded代码热更新实践和原理分析

    1.引言 开发者在编码效率和快速迭代中的痛点场景包括: 修改代码后,需要频繁重启应用,导致开发效率低下: 实时调试时,不能立即看到代码修改的结果: 大型项目中,重启的时间成本较高. 针对这些问题,本文 ...

  9. Python 学习路线图

    Python 学习路线图 在这个系列笔记中,我将陆续整理自己在学习 Python 编程语言及其框架的过程中留下的笔记和代码,目的是掌握如何在生产环境中利用各种领域的第三方框架来快速开发应用程序.和大多 ...

  10. 【SpringBoot】定时任务

    SpringBoot实现定时任务 SpringBoot创建定时任务,目前主要有以下三种实现方式: 基于注解(@Scheduled): 基于注解@Scheduled默认为单线程,开启多个任务时,任务的执 ...