|
|
本帖最后由 蛀牙の小蓝 于 2025-8-11 08:52 编辑
我有一个朋友,真是一个朋友,他想玩的小黄油在这个app上,新注册用户可以免费下载一次,如果开会员可以免费下载以及看奇怪的漫画和动漫。
我正经人怎么可能用这种app,不过刚好最近在学习逆向,想着能不能搞一下,试试就逝世!
先把app要过来,看下有没有加固,啥框架
没有加固很好,但是居然是flutter,之前没有接触过,听说不好处理,来去翻翻论坛有无大佬提供点思路。
这里找了Sunika大佬的flutter教程贴:https://bbs.binmt.cc/thread-138529-1-1.html,那就拿这个软件来试手当学习了!
开始破解
破解思路 一:
改会员状态,通过应用ui和部分会员弹窗提示文案,最终定位到 VipStatusManager --》isVip --》vip_status_manager.dart --》isVipEffective() --》user_vip_status.dart ,疑似判断会员状态。
修改了状态后,本地虽然显示解锁了会员,但是游戏、漫画和动漫都无法正常观看还是非会员,目测是服务器验证了(调了会员到期时间也一样)!
纯粹的本地自慰破解了(残念
本来到这里都已经准备放弃了,但是突然想到这个app注册的新用户会有半天的会员体验时间,而且一台新设备首次注册账号都是自动生成的系统账号,不需要手机号和邮箱注册,只有二次注册才需要手机号和邮箱注册。那这里是不是有漏洞可以钻?只要我保证每次注册的时候都让app识别为新设备,那么是不是可以达到伪开通会员,说干就干!
破解思路 二:
1.从pp.txt全局搜索注册文本【继续注册】,定位到卡注册的文本位置,可以发现该文案关联的文件为【funbox/page/account/logic/login_logic.dart】
2.全局搜索文件【login_logic.dart】分析下注册逻辑
a.发现【继续注册】的拦截弹窗在 getDeviceRegisterList() 方法中,注册的代码在 _checkRegister
b.观察 _checkRegister 代码可以发现,在请求到 getDeviceRegisterList() 拦截弹窗前,有调用 getDeviceNo()和判断 ,那么这里很有可能就是获取设备id唯一标识的地方
c.getDeviceNo() 方法关联的文件为【package:bus_common/utils/device_utils.dart】
3.全局搜索文件【device_utils.dart】分析代码逻辑
a.从getDeviceNo()汇编脚本语言可以观察出,其中是先尝试读取缓存是否有设备id,没有则重新生成一个唯一的设备id标识
b.那是不是说明我们找对地方了,只要强制代码逻辑不读取缓存,每次注册都直接生成新设备id就可以了,有希望
这里把完整的getDeviceNo() 代码发出来,有几处判断懒得一个个看,直接暴力全改了:
static String getDeviceNo() {
// ** addr: 0x96a21c, size: 0xf0
// 0x96a21c: EnterFrame
// 0x96a21c: stp fp, lr, [SP, #-0x10]!
// 0x96a220: mov fp, SP
// 0x96a224: AllocStack(0x18)
// 0x96a224: sub SP, SP, #0x18
// 0x96a228: CheckStackOverflow
// 0x96a228: ldr x16, [THR, #0x38] ; THR::stack_limit
// 0x96a22c: cmp SP, x16
// 0x96a230: b.ls #0x96a2f8
// 0x96a234: r0 = LoadStaticField(0xcd0)
// 0x96a234: ldr x0, [THR, #0x68] ; THR::field_table_values
// 0x96a238: ldr x0, [x0, #0x19a0]
// 0x96a23c: cmp w0, NULL 【改为 nop 直接忽略】
// 0x96a240: b.eq #0x96a24c 【强制修改为 b #0x96a284】使其强制跳转到生成新设备id的代码位置
// 0x96a244: LoadField: r1 = r0->field_7
// 0x96a244: ldur w1, [x0, #7]
// 0x96a248: cbnz w1, #0x96a2ec 【强制修改为 b #0x96a284】使其强制跳转到生成新设备id的代码位置
// 0x96a24c: r0 = LoadStaticField(0xccc)
// 0x96a24c: ldr x0, [THR, #0x68] ; THR::field_table_values
// 0x96a250: ldr x0, [x0, #0x1998]
// 0x96a254: ldr x16, [PP, #0x40] ; [pp+0x40] Sentinel
// 0x96a258: cmp w0, w16
// 0x96a25c: b.eq #0x96a300
// 0x96a260: LoadField: r1 = r0->field_13
// 0x96a260: ldur w1, [x0, #0x13]
// 0x96a264: DecompressPointer r1
// 0x96a264: add x1, x1, HEAP, lsl #32
// 0x96a268: LoadField: r0 = r1->field_7
// 0x96a268: ldur w0, [x1, #7]
// 0x96a26c: cbnz w0, #0x96a2e8 【强制修改为 b #0x96a284】使其强制跳转到生成新设备id的代码位置
// 0x96a270: r1 = "custom_device_no"
// 0x96a270: add x1, PP, #0x14, lsl #12 ; [pp+0x14bf0] "custom_device_no"
// 0x96a274: ldr x1, [x1, #0xbf0]
// 0x96a278: r0 = getString()
// 0x96a278: bl #0x67b5e4 ; [package:sp_util/sp_util.dart] SpUtil::getString
// 0x96a27c: LoadField: r1 = r0->field_7
// 0x96a27c: ldur w1, [x0, #7]
// 0x96a280: cbnz w1, #0x96a2e4 【强制修改为 b #0x96a284】使其强制跳转到生成新设备id的代码位置
// 0x96a284: r1 = Null
// 0x96a284: mov x1, NULL
// 0x96a288: r2 = 4
// 0x96a288: mov x2, #4
// 0x96a28c: r0 = AllocateArray()
// 0x96a28c: bl #0xc076fc ; AllocateArrayStub
// 0x96a290: r16 = "type"
// 0x96a290: ldr x16, [PP, #0x27a0] ; [pp+0x27a0] "type"
// 0x96a294: StoreField: r0->field_f = r16
// 0x96a294: stur w16, [x0, #0xf]
// 0x96a298: r16 = "deviceType"
// 0x96a298: add x16, PP, #0x14, lsl #12 ; [pp+0x14bf8] "deviceType"
// 0x96a29c: ldr x16, [x16, #0xbf8]
// 0x96a2a0: StoreField: r0->field_13 = r16
// 0x96a2a0: stur w16, [x0, #0x13]
// 0x96a2a4: r16 = <String, dynamic>
// 0x96a2a4: ldr x16, [PP, #0x1da8] ; [pp+0x1da8] TypeArguments: <String, dynamic>
// 0x96a2a8: stp x0, x16, [SP]
// 0x96a2ac: r0 = Map._fromLiteral()
// 0x96a2ac: bl #0x48a200 ; [dart:core] Map::Map._fromLiteral
// 0x96a2b0: str x0, [SP]
// 0x96a2b4: r1 = Instance_Uuid
// 0x96a2b4: add x1, PP, #0x12, lsl #12 ; [pp+0x12f58] Obj!Uuid@b184f1
// 0x96a2b8: ldr x1, [x1, #0xf58]
// 0x96a2bc: r4 = const [0, 0x2, 0x1, 0x1, options, 0x1, null]
// 0x96a2bc: add x4, PP, #0x14, lsl #12 ; [pp+0x14c00] List(7) [0, 0x2, 0x1, 0x1, "options", 0x1, Null]
// 0x96a2c0: ldr x4, [x4, #0xc00]
// 0x96a2c4: r0 = v4()
// 0x96a2c4: bl #0x7e6140 ; [package:uuid/uuid.dart] Uuid::v4
// 0x96a2c8: mov x2, x0
// 0x96a2cc: r1 = "custom_device_no"
// 0x96a2cc: add x1, PP, #0x14, lsl #12 ; [pp+0x14bf0] "custom_device_no"
// 0x96a2d0: ldr x1, [x1, #0xbf0]
// 0x96a2d4: stur x0, [fp, #-8]
// 0x96a2d8: r0 = putString()
// 0x96a2d8: bl #0x84f664 ; [package:sp_util/sp_util.dart] SpUtil::putString
// 0x96a2dc: ldur x1, [fp, #-8]
// 0x96a2e0: b #0x96a2e8
// 0x96a2e4: mov x1, x0
// 0x96a2e8: mov x0, x1
// 0x96a2ec: LeaveFrame
// 0x96a2ec: mov SP, fp
// 0x96a2f0: ldp fp, lr, [SP], #0x10
// 0x96a2f4: ret
// 0x96a2f4: ret
// 0x96a2f8: r0 = StackOverflowSharedWithoutFPURegs()
// 0x96a2f8: bl #0xc07804 ; StackOverflowSharedWithoutFPURegsStub
// 0x96a2fc: b #0x96a234
// 0x96a300: r9 = _androidInfo
// 0x96a300: add x9, PP, #0x14, lsl #12 ; [pp+0x14c08] Field <Device._androidInfo@716315110>: static late (offset: 0xccc)
// 0x96a304: ldr x9, [x9, #0xc08]
// 0x96a308: r0 = LateInitializationErrorSharedWithoutFPURegs()
// 0x96a308: bl #0xc08034 ; LateInitializationErrorSharedWithoutFPURegsStub
}
使用 DisElf2 打开【libapp.so】库文件,按照上述思路修改逻辑后保存文件,将 libapp.so 替换进apk后重新打包签名,至此大功告成!!!!
就可以正常使用会员功能拉,如果会员到期直接清空缓存后又双叒叕是一条好汉!
原包和破解包就不发了,怕被查水表
补充个改后的4.0.5版本的libapp.so文件,有需自取:
https://www.123684.com/s/DSEnTd-ZZl0v 提取码:WN9u
叠个甲,免责声明,本教程仅供技术交流,无不良影响,无引流推广,仅提供逆向思路
再次感谢Sunika大佬提供的教程,真的学到东西了!
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|