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. MMCM and PLL Dynamic Reconfiguration

    Reconfiguration is performed through the DRP. The DRP provides access to the configuration bits that ...

  2. 用rust 写一个jar包 class冲突检测工具

    Rust很适合写命令行工具,特别是使用clap crate 更加方便,这篇文章介绍使用rust写一个jar包class冲突检测的工具.项目地址: https://github.com/Aitozi/j ...

  3. springboot 整合jdbc

    在springboot底层无论关系型还是非关系型数据库都是用spring-data进行交互 新建: 通过spring initialer勾选重要依赖jdbc api和mysql driver: 源码分 ...

  4. macbook苹果m1芯片训练机器学习、深度学习模型,resnet101在mnist手写数字识别上做加速,torch.device("mps")

    apple的m1芯片比以往cpu芯片在机器学习加速上听说有15倍的提升,也就是可以使用apple mac训练深度学习pytorch模型!!!惊呆了 安装apple m1芯片版本的pytorch 然后使 ...

  5. Spectre.Console-处理依赖注入

    引言 之前说的做自动记录 Todo 执行过程中消耗的时间的Todo 项目,由于想持续保持程序执行,就放弃了 Spectre.Console.Cli,后来随着命令越来越多,自己处理觉得很是麻烦,想了想要 ...

  6. 【Python】如何在FastAPI中使用UUID标记日志,以跟踪一个请求的完整生命周期

    为什么要使用uuid标记日志? 在分布式系统中,一个请求可能会经过多个服务,每个服务都会生成自己的日志.如果我们只使用普通的日志记录,那么很难将这些日志串联在一起,以至难以跟踪一个请求的完整生命周期. ...

  7. Java中读取用户输入的是谁?Scanner类

    前言 我们在初学 Java 编程的时候,总是感觉很枯燥乏味,想着做点可以交互的小系统,可以让用户自由输入,系统可以接收做出反映.这就要介绍一下 Java 中的 Scanner 类了. 一.Scanne ...

  8. @Override注解的使用

    先看看@Override注解在Java SE中的声明: package .lang; import java.lang.annotation.*; @Target(ElementType.METHOD ...

  9. OSI七层协议剩余、socket模块、半连接池

    传输层之TCP与UDP协议 TCP与UDP都是用来规定通信方式的 通信的时候可以随心所欲的聊 也可以遵循一些协议符合要求的聊 随心所欲的聊:文字 图片 视频 遵循一些协议:开头带尊称 首行空两个 只准 ...

  10. 1.1 Metasploit 工具简介

    Metasploit 简称(MSF)是一款流行的开源渗透测试框架,由Rapid7公司开发,可以帮助安全和IT专业人士识别安全性问题,验证漏洞的缓解措施,并管理专家驱动的安全性进行评估,提供真正的安全风 ...