[Honkai 3]Ver 1.6.0beta

发布于 2019-07-22  4.19k 次阅读


0x00 前言

[+]本文作者:Floeice

碎碎念:直到不久前该游戏的libil2cpp.so才加上了壳,意味着很久以前部分测试结果可以解禁了,在此感谢@地底的大白。

2017/06/21: 1.6beta的服务器checksum已经上线,但这并不意味着文件修改已经无从下手。通过分析发包流程,在Arm层精心构造自己的代码进行替换,亦可达到修改并绕过检测的目的。本文将详细阐述当前版本下(20170616-Beta-versions-v1.6.1_summerversion.apk)的文件修改过程及原理

0x01 前期准备

1.手工还原AntiDumper

过程省略(这玩具就是我自己写的

2.动态调试准备
am start -D -n com.miHoYo.enterprise.NGHSoDBeta/com.miHoYo.overridenativeactivity.OverrideNativeActivity
jdb -connect com.sun.jdi.SocketAttach:port=8700,hostname=localhost

以关键点IpInfo为例:

0xDEA260 NetWorkManager$$RequestPlayerToken
r0 = Final IpInfo;
0xDEB300 NetWorkManager$$FormatIpInfo
r1 = md5(libil2cpp.so+libulua.so)_md5(nativelib filename);
r2 = uid;
r0 = r1 + r2;
Final IpInfo = md5(r0);

因此,运行原版游戏,在登录界面直接附加动态调试来获得三个关键函数ipv4stripv6stripinfostr(r1)以及uid(r2),并记录下正确的set_ipv4_strset_ipv6_str以及set_ip_info_str的值

3.记录
ipv4*
r1 = 5cd281a5 //MANIFEST.MF crc32
r2 = 4970492 //uid
r1 + r2 = 5cd281a54970492
md5(r1 + r2) = c95618621484405e3cc0044545fb19e4
ipv6*
r1 = c8743787 //sign
r2 = 4970492 //uid
r1 + r2 = c87437874970492
md5(r1 + r2) = cdf18c881f1abe080a5cc74603516668
ipinfo*
r1 = 43dd90f05d25d8ec58a8f34196fda207_fc34b9e0c887f9b8ef1b6d334e19619b
r2 = 4970492 //uid
r1 + r2 = 43dd90f05d25d8ec58a8f34196fda207_fc34b9e0c887f9b8ef1b6d334e19619b4970492
md5(r1 + r2) = 23efa123135dca26b4b445e02b57db87

0x02 Patch so

可见,checksum计算中均加入了玩家的uid,这意味着即使能做到修改发包结果,欺骗服务器并完成登录,其结果也不具备通用性(uid不同),一定程度上限制了破解的流通。但如果Patch掉UIUtil$$GetIPInfoStrUIUtil$$GetIpv4Str以及UIUtil$$GetIpv6Str三个关键函数,就能绕开uid的限制,甚至能够制作无需root的破解版客户端

下文主要涉及函数UIUtil$$GetIPInfoStr的Pacth流程

1.获取script.py

利用改版Il2cppdumper获得新的script.py,输出String的DefineStart以及Index,以最后一条String为例,已知其长度为74

Define = 0x7808,Start = 0x32BE9

其中Define是该String在metadata中定义的位置

metadata addr : 0x7808

根据il2cppStringLiteral的定义:

可知length = 0x4A,dataIndex = 0x14481

IlcCppGlobalMetadataHeader部分定义
Il2CppDumper代码片段
metadata addr : 0x10,stringLiteraDataOffset
Start = stringLiteralDataOffset + dataIndex = 0x14481 + 0x1E768 = 0x32BE9

得到该String在metadata中的起始位置为0x32BE9 ,如下:

同时得到其在libil2cpp.so中的引用位置0x28e9100:

got表地址为0x28956C8:

2.对比测试

对上述metadata和libil2cpp.so做修改如下(还原AntiDumper,修改String)

length = 0x41
new String = 43dd90f05d25d8ec58a8f34196fda207_fc34b9e0c887f9b8ef1b6d334e19619b

将修改过的metadata和libil2cpp.so放入Apk内并安装,尝试登录并触发服务器checksum检测,得到以下对比:

原版 Normal GetPlayerTokenReq
修改后 Abnormal GetPlayerTokenReq

可知最终触发检测的是UIUtil$$GetIPInfoStr,原因:libil2cpp.so内容被修改

3.修改 UIUtil$$GetIPInfoStr(Arm Patch)

可知最终目的为修改UIUtil$$GetIPInfoStr函数,调用got表0x28956C8,返回固定正确的字符串

1.首先确定前四句,现场保护后必须先调用il2cpp::vm::MetadataCache::InitializeMethodMetadata(uint)完成初始化

第一句从0x1011CFC开始,Index = 32264 = 0x7E08

第四条语句BL addr0x1011D08

函数j__ZN6il2cpp2vm13MetadataCache24InitializeMethodMetadataEj addr0x35713C

SrcAddr + (x << 2 + 8) = DstAddr
x = ((DstAddr - SrcAddr) - 8) >> 2

高低对换后+EB -> BL,因此第四条语句应为0B 15 CD EB

Arm Hex(1-4): 
F0 4F 2D E9 //STMFD SP!, {R4-R11,LR}
1C B0 8D E2 //ADD R11, SP, #0x1C
08 0E 07 E3 //MOV R0, #0x7E08
0B 15 CD EB //BL InitializeMethodMetadata

2.接下来两句用于取got表地址,令r7 = _GLOBAL_OFFSET_TABLE_的地址, 第五句从0x1011D0C开始

PC = 当前语句 + 0x8 -> PC = 0x1011D0C + 0x8 = 0x1011D14

该函数结尾为0x1012300,可用于存放数据

0x1012300 - PC = 0x5EC,因此第五句为LDR R0, [PC, #0x5EC]

_GLOBAL_OFFSET_TABLE_ addr: 0x28B06B0

第六句从0x1011D10开始

PC = 0x1011D10 + 0x8 = 0x1011D18
0x28B06B0 - PC = 0x189E998

因此off_1012300应改写为98 E9 89 01(无变化),第六句为ADD R7,PC,R0

Arm Hex(5-6): 
EC 05 9F E5 //LDR R0, [PC, #0x5EC]
00 70 8F E0 //ADD R7, PC, R0

3.接下来三句用于取字符串地址,第七句从0x1011D14开始,PC = 0x1011D1C,将数值部署在off_1012308

0x1012308 - 0x1011D1C = 0x5EC

因此第七句为LDR R8, [PC, #0x5EC],第八句为LDR  R6, [R7, R8],目的是在执行这两句后 r6 = got表 -> 0x28956C8,即指向new string

r7 = 0x28B06B0
r8 = r6 - r7 = 0xFFFE5018

因此off_1012308应改写为 18 50 FE FF

第九句为LDR R0,[R6]

Arm Hex(7-9): 
EC 85 9F E5 //LDR R8, [PC, #0x5EC]
08 60 97 E7 //LDR R6, [R7, R8]
00 00 96 E5 //R0, [R6]

4. 结尾,现场恢复

Arm Hex(10-11): 
1C D0 4B E2 //SUB SP, R11, #0x1C
F0 8F BD E8 //LDMFD SP!, {R4-R11, PC}
总览

0x03 登陆测试

Login success

0x04 相关工具

Il2CppDumper