硕士生
 
- 金币
- 3830
- 好评
- 25
- 信誉
- 120
|
发表于
2025-12-21 11:37:53
来自手机
|
显示全部楼层
| 阅读模式
来自 广东
本帖最后由 tuandgame 于 2025-12-21 11:37 编辑
纯文本版逆向小记
——从一次 NullPointerException 到完整解锁八字 App 的流水账
0x00 开场白
按照正则那么改的话,App 一点马年进程就崩:
`java.lang.NullPointerException: substring(0, 6) on a null object`
日志指向 `MainActivity.Y()`,于是有了这趟补课。
0x01 定位崩溃
1. 看堆栈:
`com.nfzhouyi.Liunian.MainActivity.Y()` 里调了 `String.substring(int, int)`,但字符串是 null。
2. 反编译后发现 Y() 在拼 HTML 表格,连续 `aget-object` 从静态数组
`Lv/h->y0[]`、`c0[]`、`e0[]`、`D0[][]` 里取元素,一旦元素为 null 立刻崩。
3. 根源:这些数组只在注册成功后的初始化代码里赋值;我为了调试跳过注册,导致数组全是空。
0x02 注册按钮到底干什么
- `activity_register$c->onClick()` 只做两件事:
a) 校验 15 位注册码(年月日 + 机器码拼接比对);
b) 把加密后的注册码写进 `SharedPreferences("pcr")`,置标志位 `Lv/a.s = true`。
- 不会帮你填八字数组!所以即使绕过校验,后面照样空指针。
0x03 注册码算法(已逆向)
15 位数字分段:
```
8 2029 → 82029 (年:首位 8/9 决定 20xx/19xx)
4 07 → 40707 (月:首位 4/5 决定 0x/1x)
3 18 → 31818 (日:首位 3/4 决定 0x/1x)
合并:820294070731818
```
接着把机器码去掉 `-` 后与前 5 位拼成 9 位字符串,再转小写,要求注册码 15 位完全等于这 9 位——长度都不一样,原版逻辑写崩,永远过不了。
0x04 最快 Bypass —— 让校验函数永远 true
目标方法:`Lv/d;->f(Ljava/lang/String;Ljava/lang/String;)Z`
Smali 补丁(两行):
```
const/4 p0, 0x1
return p0
```
重新打包 → 安装 → 随便输 15 位数字 → 提示“注册成功”。
0x05 解锁任意日期
注册成功只置了标志位,排盘函数 `Z()V` 里仍硬编码 2000-05-01。
在注册成功的分支末尾手动再调一次 `Z()`:
```
invoke-static {}, Lv/h;->Z()V
```
或者更早的位置直接置标志:
```
const/4 v0, 0x1
sput-boolean v0, Lv/a;->s:Z
```
这样 `Z()V` 会走 else 分支,用真实生日、地区、真太阳时重新排盘,功能完全解锁。
0x06 结论 & 通用思路
1. NPE 先找 null 数组/字符串,回溯谁负责初始化。
2. 注册码校验看不懂就打印参数和返回值,往往有惊喜(或惊吓)。
3. 绕过校验 ≠ 功能全开,状态标志 + 初始化函数都要照顾到。
4. 真不想算注册码,就永久返回 true,省时省力,记得把初始化流程补完即可。
—— END |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|