某学习版游戏平台逆向分析

引言

想玩游戏了,某下载平台需要花钱购买激活码才能显示下载链接

于是就开破

正文

查壳

拖入exeinfope查查壳先,发现啥也没有,如下图

就一简单.NET程序,倒是方便,比上次破的某excel插件还简单

开始逆向

激活码判断逻辑

拖入dnSpy开始狠狠的分析,C#这种解释语言就是好,连变量名和函数名都给你安排的明明白白

见过中英文混输的,没见过拼音和英语混的(迷惑外国友人??)

全程序集查找“邀请码”三个字,得出下述函数

最后一个QueDing_click起名真是难绷, 进去看看,发现内含下述代码 

if (YQMB.tt1.Length > 6)
{
	this.DengLu();
	return;
}

盲猜一波这个 YQMB.tt1.Length就是邀请码的长度,先检查输入够不够7位,够了再执行条件体内的代码,至于DengLu()函数,我相信此处无需多言了

跟进DengLu(),熟悉的中文跃入眼帘。

if (this.IsLoging)
{
	MessageBox.Show("正在激活,请勿重复操作!");
	return;
}

看来是来到了关键函数了,再往下面翻翻,发现有post请求,如下述代码

HTTP http = new HTTP();
HttpItem item = new HttpItem
{
	Method = "POST",
	Postdata = "Action=CheckMachineCode&UserName=" + ServiceLocator.Current.GetInstance<MainViewModel>().tt1 + "&MachineCode=" + YQMB.MachineCode,
	URL = Ye1.tt1a005,
	ContentType = "application/x-www-form-urlencoded"
};
HttpResult html = http.GetHtml(item);
JObject back = JsonConvert.DeserializeObject<JObject>(html.Html);
if (back == null)
{
	MessageBox.Show("连接失败");
	return;
}

看来是把用户名和机器码发到服务器去验证,此处可以下个断调试一下,看看发送和接收的数据都长啥样 

其中UserName就是我瞎jb乱输的激活码,MachineCode是程序计算的机器码,服务器的返回内容如下(已unicode解码):

{"State":false,"Msg":"激活失败,本邀请码错误","Data":null,"Code":0}

其中的Msg字段刚好对应了提示的弹框

由此看来邀请码的计算算法是在服务器上实现的,搞不了了,不过没有关系,根据http请求紧接着的代码:

bool state = (bool)back["State"];
if (state)
{
	this.Login(ServiceLocator.Current.GetInstance<MainViewModel>().tt1, YQMB.MachineCode);
	string text = back["Data"].ToString();
	if (!string.IsNullOrEmpty(text) && text == "104")
	{
		int num = int.Parse(back["Msg"].ToString());
		if (num > 0)
		{
			ServiceLocator.Current.GetInstance<MainViewModel>().HuanBang = string.Concat(new string[]
			{
				"本激活码【",
				ServiceLocator.Current.GetInstance<MainViewModel>().tt1,
				"】",
				back["Msg"].ToString(),
				"天后可获得换绑机会"
			});
		}
		if (num == 0)
		{
			ServiceLocator.Current.GetInstance<MainViewModel>().HuanBang = "本激活码【" + ServiceLocator.Current.GetInstance<MainViewModel>().tt1 + "】支持换绑其他电脑";
		}
		return;
	}
	return;
}

可知程序是通过判断服务器返回内容中的State字段来判断邀请码是否正确,我们可以在这里下手术刀,判断state的语句的IL中间代码如下

4	0008	ldfld	bool YQMA.YQMB/'<>c__DisplayClass12_0'::state
5	000D	brfalse	80 (0114) ldarg.0 

我们只需要把brfalse指令改成brtrue即可,更改后的逻辑是:如果state是false,那么就激活,反正目的达到了就行,我们可以直接右键-反转更改快速改了它

更改后,源代码窗口自动的就同步更改 

或者直接在brtrue判断指令前面加一条ldc.i4.0指令,直接把false压入堆栈,这样就不管state是啥,if都满足条件了,如图

经过分析,发现程序有两处判断,也就是要改两个state,函数调用图解如下

至此,只要随便输入任意邀请码,都可以激活,也就可以正常显示了。

如果只想玩到游戏的话,那么到这里其实工作就可以结束了。接下来对程序的剩余部分再行分析一些。

检测更新逻辑

程序在启动时会检查更新,如果有更新会提示更新,更新服务器地址加了密,经动态调试直接解密得地址为阿里云服务器

内含配置文本如下(节选)

 

可知判断更新和核心配置就从这儿下载的,把更新的代码全部删掉即可屏蔽更新。

激活码加密逻辑

所有游戏的下载链接、激活码等信息都包含在服务器的一个文件中,程序只需要拉取这个文件即可向用户显示出来,发现代码中存在一行这样的玩意儿:

string text = Path.Combine(Services.AppDataDirectory, "WenJian.json");

用everything全盘搜索此文件,果然在系统临时目录找到了,打开一看,是部分DES加密的

{"Version":"202406022152024112","Content":[{"Id":1,"BH":"wcM2a5Zm+ng=","MM":"ocMsr4JndLc=","Name1":"oiGjoLIwegJXxLL8UwjTz+LkxsysmZa8","Name2":"oiGjoLIwegJXxLL8UwjTz4fP0QLEb6IP","B

不过没有关系,密钥已经写到程序里面了,如下

// Token: 0x04000282 RID: 642
private static byte[] Keys = new byte[] { 18, 52, 86, 120, 144, 171, 205, 239 };

// Token: 0x04000283 RID: 643
private static string DefEncryptKey = "consmkey";

直接丢给GPT让它帮写解密的代码,并写入CSV文件,如下:

import time
import csv
from Crypto.Cipher import DES
import base64
import json

def decrypt(ciphertext_base64):
    # DES密钥向量
    keys = bytes([18, 52, 86, 120, 144, 171, 205, 239])

    # 加密密钥
    encrypt_key = "consmkey"

    # 创建DES加密器
    cipher = DES.new(encrypt_key.encode('utf-8'), DES.MODE_CBC, IV=keys)

    # 待解密的密文
    ciphertext = base64.b64decode(ciphertext_base64)

    # 解密
    plaintext = cipher.decrypt(ciphertext)

    # 去除填充
    plaintext = plaintext.rstrip(b'\x08')
    plaintext = plaintext.rstrip(b'\x07')
    plaintext = plaintext.rstrip(b'\06')
    plaintext = plaintext.rstrip(b'\05')
    plaintext = plaintext.rstrip(b'\04')
    plaintext = plaintext.rstrip(b'\03')
    plaintext = plaintext.rstrip(b'\02')
    plaintext = plaintext.rstrip(b'\01')
    plaintext = plaintext.rstrip(b'\00')
    # 打印解密后的明文

    return plaintext.decode('utf-8')

with open ("WenJian.json", "r", encoding="utf-8") as f:
    data = json.load(f)
f.close()
with open('game.csv', 'w', newline='', encoding='utf-8') as file:
    writer = csv.writer(file)

    for i in data['Content']:
        data = []

        name1 = i['Name1']
        print('当前解密:',decrypt(name1))
        data.append(decrypt(i['BH']))
        data.append(decrypt(name1))
        data.append(decrypt(i['Name2']))
        data.append(decrypt(i['MM']))
        data.append(decrypt(i['RongL']))

        writer.writerow(data)

file.close()

结果经过excel美化后,如下

至此,整个程序就分析的大差不差了

结语 

过程中需注意dnSpy对于多线程的支持不是很好,若想查看异步的代码需要在视图-选项-反编译器中勾选反编译异步方法显示编译器生成的隐藏类型和方法,如下图,默认是不勾的

而且,在调试的时候多线程经常出现奇奇怪怪的问题,建议不要用dnspy的调试,而是把文件写出后直接运行,问题就减少了大半,坏处就是不能单步代码了,略考验代码功底哈哈哈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值