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. AcWing 3729. 改变数组元素

    给定一个空数组 V 和一个整数数组 a1,a2,-,an. 现在要对数组 V进行 n次操作. 第 i次操作的具体流程如下: 从数组 V尾部插入整数 0.    2.将位于数组 V末尾的 ai 个元素都 ...

  2. Updates were rejected because the tip of your current branch is behind

    最近本地一个flutter项目因为当时使用可视化创建的时候出了一些问题,但是起初没有注意,后来因为需要新增一个语音插件,需要修改原生android MainActivity.java,才发现这个目录根 ...

  3. Iframe在Vue中的状态保持技术

    引言 Iframe是一个历史悠久的HTML元素,根据MDN WEB DOCS官方介绍,Iframe定义为HTML内联框架元素,表示嵌套的Browsing Context,它能够将另一个HTML页面嵌入 ...

  4. MassTransit实现Saga模式概览

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

  5. c# 如何将枚举以下拉数据源的形式返回给前端

    前言: 相信各位有碰到过与我类似的问题,当表中存一些状态的字段,无非以下几种形式1.直接写死 如: 正常:1,异常:2 ,还有一种则是写在字典中,再或者就是加在枚举上,前两者对于返回下拉数据源来说比较 ...

  6. gitlab docker升级报错

    背景 使用docker部署gitlab(9.5.4)后,发现合并代码有问题 日志: 看gitlab官网此问题已修复,由于上传了一批代码,又懒得重建,决定对gitlab升级 docker启动命令: do ...

  7. in用不用索引,啥时候能用啥时候不能用,一文说清

    in/or到底能不能用索引应该是肯定的,但有时生效有时不生效,这个能不能量化计算?这是本文想讨论和解答的问题. in到底用不用索引感觉像一桩悬疑片!古早时期的面经,统一说不走索引,在一些程序员脑海中从 ...

  8. Kubernetes(k8s)访问控制:身份认证

    目录 一.系统环境 二.前言 三.Kubernetes访问控制 四.身份认证简介 五.身份认证 5.1 配置客户端机器 5.2 使用base auth的方式进行认证 5.3 使用token的方式进行认 ...

  9. Redis基础(二)——列表操作、redis管道、Django中使用redis

    Redis列表操作 ''' lpush(name,values) rpush(name, values) 表示从右向左操作 lpushx(name,value) rpushx(name, value) ...

  10. 如何在 Windows10 Professional 服务器上搭建自己的 Git 服务器。

    一.简介 以前,在别家的公司,一般早就把源代码管理工具搭建好了,很少有机会自己搭建一套.最近,公司也许要把现在不少的源码进行管理,于是我打算自己搭建源代码管理服务器.说起源代码管理,当然有很多中解决方 ...