免责声明:本博文相关内容已涉及 lowiro 的实际利益。本博文内容仅用于个人软件安全研究与学习。请勿将博文内容用于商业或者非法用途,如果阁下愿意继续阅读,请您承诺将为自己的全部行为负责。
本文基于Arcaea 3.7.0c(及以前的)(或许还会有以后的)版本,arm64-v8a
工具
IDA
一些功能&快捷键:
- 数据库快照:存放数据库的历史更改
Ctrl+Shift+T
- F5:快速切换至伪代码
插件
Bindiff
咕
Keypatch
咕
Frida
咕
gdb
众所周知,gdb是一个功能强大的调试器,我们可以用它来调试native层的程序
安装
首先,最好不要用Windows,坑太多了(如果你成功了请教教我)
我们可以用WSL+Ubuntu,以下内容皆基于此基础展开`
由于目标是aarch64
架构,所以需要多架构的gdb(这也就是不用Windows的原因)。执行sudo apt install gdb-multiarch
,随后执行gdb-multiarch
以确定安装成功
gdbserver可以在Android NDK的prebuilt/android-arm64/gdbserver/
目录下找到,也可以用我上传的版本(gdbserver 7.11 for arm64,来自NDK 19c for linux)
把gdbserver丢到/data/local/tmp/
目录下,赋予777权限
连接
手机端打开arc、usb调试开关
(adb可以用Windows上的)使用adb连接手机(有线/无线),输入adb forward tcp:23946 tcp:23946
进入shell(adb shell
),切换root(su
)
执行pid=`ps | grep moe | awk '{print $2}'`;echo $pid
,得到的数字即为arc的PID(在一些设备上,ps
要改为ps -A
)
执行cat /proc/$pid/maps | grep cocos | grep '[0-9a-f]*' -o | sed -n '1,1p'
,得到一个16进制数(形如7f68dcd000
),把他丢python里,记为base_addr
,这就是so文件的基址[需要验证]
执行/data/local/tmp/gdbserver :23946 --attach $pid
,开启gdb服务端
一行版,用于开始程序后立刻attach:pkg="moe.low.arc";killall $pkg;am start -W $pkg/low.moe.AppActivity>/dev/null;pid=`pidof $pkg`;cat /proc/$pid/maps | grep cocos | grep '[0-9a-f]*' -o | sed -n '1,1p';/data/local/tmp/gdbserver :23946 --attach $pid
(注意:上述命令可能因环境不同而变化,请按需更改!)
打开gdb-multiarch
,输入target remot localhost:23946
,等待gdb加载(中途的分页按c
以继续)
加载完成后,光标停留在(gdb)
后面,此时可以输入指令
使用
进程暂停时输入c
以恢复进程运行,进程运行时输入Ctrl+C
以暂停进程
在IDA找到你需要下断点的位置(形如0x1F3FF
),记作offset
,进入python,执行base_addr+offset
,得到一个十进制数(形如547220276223
),回到gdb输入b *547220276223(得到的十进制数)
下断点
输入i reg
可以查看寄存器的值,如果想查看含向量寄存器和浮点寄存器的值,则需要使用i all-reg
用ni
替代n
,用si
替代s
来步进
更多用法可参考其它教程,比如这篇
核心破解
需要Xposed框架(作用域:Android系统),用于绕过签名、版本校验(可以直接安装签名错误或没有签名的apk)
下载地址:
- 安卓10-11:https://github.com/LSPosed/CorePatch/releases/tag/3.5
- 安卓7-10:https://www.lanzoux.com/i7z2rqh
- 安卓4-7:https://www.lanzoui.com/iaKd1jdk6pi
NS相关
hactool
咕
nx2elf & elf2nso
咕
内容
.so相关
ScoreState
结构体
警告:本表可能存在问题,如发现问题请提Issue
(原版来自西城飘雪的博客)
struct ScoreState {
pointer vtable; // 0 0 指向vtable的指针 占8字节
unsigned int _referenceCount;//8 8 cocos2d内部使用
//skip
int noteCount; // 16 10
int maxCombo; // 20 14
int score; // 24 18
int score_check; // 28 1c score / 3
float hp; // 32 20
float unk36 //36 24
float unk40 //40 28
BYTE unk44 //44 2c
BYTE unk45 //45 2d
float unk48 // 48 30 貌似恒为0
float unk52 //52 34 仅风暴
float unk56 //56 38 仅风暴/Ether Strike异象
float unk60 //60 3C 仅风暴 回忆系数*物量
float unk64 //64 40 仅风暴 初始化:100-unk60 if unk60<100 else 0;更新:继承上次更新的v16
float hit_hps //68 44 仅风暴 将在击中note时增加应增加的回忆度(pure:2*回忆系数 far:1*回忆系数)
float nonhit_hps //72 48 仅风暴 将在未击中note时减少应扣除的回忆度(同一般Hard血条一致)
float unk76 //76 4c 仅风暴
int combo; // 80 50
int shiny_pure; // 84 54
int pure; // 88 58
int far; // 92 5c
int lost; // 96 60
int shiny_pure_check // 100 64 shiny_pure/6
int pure_check // 104 68 pure/5
int far_check // 108 6c far*2
int lost_check // 112 70 lost*6
int far_late //116 74 far的late数
int far_early //120 78 far的early数
int pure_late //124 7c 小p的late数
int pure_early //128 80 小p的early数
bool(?) unk132 //132 84
float hp_base_value // 136 88 回忆系数(风暴时为回忆系数*2)
int unk92 //13a 92
int skill_type; // 152 98 //0普通,1简单,2困难,5过载,6风暴
int unk156 //156 9c
LogicChart* lc //160 A0 占8字节
GameResult* gr //168 a8
int lasttime //176 b0 上次调用ScoreState::update的时间(毫秒)
bool unk180 //180 b4
int unk184 //184 b8
int unk188 //188 bc
CharacterAbility* ca //192 C0 占8字节
bool unk200 //200 c8
} ScoreState;
//铺面等级:*(int *)(*(__ *)(TheLogicChart_ptr + 24) + 136LL)
GameResult
结构体
struct GameResult {
pointer vtable; // 0 0 指向vtable的指针 占8字节
//skip
int unk12//12 xx
int score;//16 10
int shiny_pure//20
int pure//24
int far//28
int unk32//32
int unk36//36
bool unk56//56 xx
int unk80//80 xx
} GameResult;
去除Tempestissimo
的硬编码
已过时,删除
Frida相关
咕
gdb相关
貌似没啥x