首页
社区
课程
招聘
[原创]从某乐跑看高德SDK
发表于: 1天前 753

[原创]从某乐跑看高德SDK

1天前
753


如何定位系统定位的链路呢?
目前我们不知道APP的定位是从哪里来的,所以需要先把系统的LocationManager的入口铺满
安卓APP获取自身定位有两个方法
根据gp服务提供的FusedLocationProviderClient(融合定位提供器),常用函数有两个:
1.获取一次当前位置
Java:fusedLocationProviderClient.getLastLocation(),它返回设备最后一次已知的缓存位置,适合"打开页面时定位到当前位置"的场景(如地图初始化)。
2.持续监听位置变化
requestLocationUpdates(LocationRequest, LocationCallback),在 onLocationResult 回调里拿最新坐标:

还有系统原生方案:
框架自带的LocationManager:

所以现在我们可以把LocationManager给铺满了

这运行之后我们就可以拿到两个东西了

5参数重载,并且高德同时向系统注册了两个provider的持续定位,gps和network
高德自己 new 了一条定位线程(b2$e.run → Looper.loopOnce),AmapLocationManager 的 Handler 收到消息(b2$d.handleMessage),转给 GpsLocation(u5.p → u5.K),最终调 LocationManager.requestLocationUpdates("gps", …)。

高德在拿到系统定位后干了什么呢?APP在哪一步介入的?
我们可以找一个数据必然经过的地方,打印调用栈

为什么选location.getlatitude呢?
不管上层怎么混淆、怎么抽象,坐标最终一定是 Location 或其子类的实例,读坐标必走 getter 或字段。getter 可 hook,字段不可 hook —— 所以选 getter。
Hook之后的输出为

到这一步我们就知道了是
系统投递 → u5$c.onLocationChanged → u5.g → AMapLocation.<init>。
现在我们要去找APP入口了,静态分析的时候找到了两条候选的线索

但是Hook的时候根本Hook不到,这是因为梆梆的加固

对于层A:

可以看到都传递到了cv函数中

所有的java2c都经过cv函数,而cv的末尾变参是方法表下标JID,所以

配合静态还原的 18851 条 JID → 方法名 映射,能实时看到 App 的完整执行流。
层B:编译态如何让Hook失效
ART里一次方法调用的路径:
调用方进行方法调用的时候,都是同一个动作:
拿到目标方法的ArtMethod,读取它的entrypoint,跳过去执行
每个 ArtMethod 有一个执行入口字段:

entry_point_from_quick_compiled_code_ 指向什么,决定这次调用怎么执行:

Frida的HOOK做了什么呢?

所以如果方法被JIT编译,并且调用点也被优化过,那么:

所以虽然改了ART里面的字段,但是bl根本不看它了
Hook对APP自身类失效了,因为梆梆java2c下这些方法被AOT/JIT编译,调用走已经生成的机器码,绕过了被替换的ArtMethod
entrypoint

层C
可以通过deoptimize才能hook到app的本身类
什么是deopt呢?
deoptimization 来自 JVM 世界的术语(HotSpot 的 deoptimization),指"从优化后的代码回退到未优化的解释执行状态",同时在回退时保留程序语义(通过 deopt 时构造解释器栈帧,把寄存器/栈上的值搬到解释器帧里)
ART里面的deopt的几种触发:

ArtMethod里面,entrypoint指向哪里,决定它怎么跑:

deopt就是把2放到3里面,deopt的入口是单独的,不能直接改成解释器入口,因为方法可能被其他线程执行,所以ART的做法为

deopt干的事情?
编译态下,调用方机器码可能是这样的

deopt做的就是让第一种形式失效

所以我们可以通过单方法降级让hook生效

现在我们就确定了整个定位的流程

什么是lbs,这不是系统的network provider

lbs = Location Based Service。这是一条完全独立于系统定位的通道 —— 高德自己发 HTTP 请求去查自己的数据库。
这个通道的输入是物理环节的指纹,周围有哪些wifi,哪个基站,输出是服务器数据库里面匹配到的参考点坐标

高德的libs定位:

在 u0.onLocationChanged 里连续 dump:

t一直在变,但是坐标不变,这证明回调是存货的,但是内容不对

为什么我们上面的已经成功了,但是坐标没有改变呢?是不是我们的hook根本没作用?
通过HOOK发现我们的数据成功被高德读取了,而且是反复读取,但是高德在做多源混合,读取了我们的gps数据,但是没有采用,而是选用了网络定位的结果

所以就是我们的gps模式一直被拒绝,所以我们需要先去切断网络定位这个通道,强制选取GPS,再观察GPS定位的错误
先 getEnumConstants()

这是知道了这个app选取的什么定位模式

现在我们需要强制app走gps模式了 App 会在两个地方设模式,都要堵:

最终得到的结果为

err=15

高德拒绝 test provider(errorCode=15)t
estprovider是系统的locationmanager允许测试提供者
一旦注册,所有监听 gps 的 App 收到的不再是真实 GNSS 数据,而是我们用 set-test-provider-location 塞进去的坐标。
但它有个先天限制:产出的每个 Location 都会被盖上 mock 标记。

但是注入后,虽然Location.getLatitude hook 读到了我们设的坐标,但是APP上面显示的距离不动
这说明我们的数据传入了应该传入的地方,但是不被接收

这里有个很重要的点:u0 收到的是 AMapLocation(高德自己的对象),不是原始 Location。所以 err=15 是高德生成的,不是系统给的。

系统的 Location 没有 errorCode 概念(Location 没有 errorCode 字段,它是 AMapLocation 才有的)。

所以这表示系统的location没问题,但是经过高德sdk处理后,报错了,当时传递出去的定位带有mock标志,怀疑是因为带有这个标志不行,于是通过Hook屏幕java mock getter

把 Location.isFromMockProvider()(旧版)和 isMock()(Android 12+,含 LocationManager/Location 的多个重载)的实现直接替换成恒返回 false。

所以证明高德的判定不读取这三个java的getter

然后我们hook setmock函数,发现从没有命中,这证明:
系统给 test provider 的 Location 打 mock 标记,不走 Location.setMock()。
那是如何打的呢?在安卓源码中,

在尝试了其他几个方法后发现从java层根本堵不住

于是考虑了重写对象
因为 AMapLocation 是高德自己生成的对象(err=15 也是它填的)。我们在它交给 App 的瞬间把字段全改掉

在出口篡改

(也可以试试 hook setmockenable,我没试)

内部转成消息投递给 b2:

b2 持有 LocationMode,分派表在静态块

网络通道的开关为一个独立判断

这就是为什么我们上面强制为Device_sensor之后prov=lbs就不行了,因为这里的if判错了

u5 启动时向系统注册

系统回调进 u5$c.onLocationChanged → 转发到 u5.g()。

u5.g

可以发现出口校验比较宽松,根本不查mock标志和satellites

通过上面可以发现有很多定位的渠道,但是高德是多源并存,然后给每个通道评分,谁的分高用谁

那么这个是如何判断谁的分数高呢?先到先用然后本地质量过滤
需要注意的是以下几点:
1.没有全局融合器,GPS结果和LBS结果都各自独立,谁先完成谁先交付
2.竞争不在通道之间,而是每个通道自己的过滤阈值里面
3.上面的案例之所以采用LBS的方案是因为LBS恒定且能通过自身的弱过滤,但是gps被发现了mock被过滤掉了

GPS

LBS

这两条通道都是有结果就交给P

u5.g中有一处规则

配合 f7994j = 240、f7995k = 80 两个常量,做的是运动连续性校验。
这两个常量从Bundle里被覆盖

阈值1: f7994j=240
这是逆地理刷新的距离门限
u5.o()`

作用:移动距离不到240m,就复用上一次的逆地理编码结果

阈值2: f7995k=30m
这是GPS上报门限
u5.t()

这条规则就是GPS定位必须和上一次定位的距离相差80m才会上报,而且有双重限制:
1.时间限制
2.距离限制

(需要注意的是,80和240是高德sdk的默认值,服务器可以更改)
更关键的是 u5 有 f(i10) 这类过滤器,配合 filters/a.java(定位滤波器)对结果做平滑/去噪。

GPS的判断首先会看卫星的数量有几个

这个值有两处作用:
1.参与mock判定
2.写进结果

同时还有其他的地方
b2.w() 在交付前组装质量报告

setInstallHighDangerMockApp(R) 值得注意——高德会检测设备上是否安装了已知的虚拟定位 App(如 Fake Location 之类),结果放进质量报告交给 App。App 可以用它做风控。

双存储:继承系统的Location但是重写全部的getter

这就代表这AmapLocation有自己的经纬度字段,与父类Location的字段并存,原生代码读取哪个取决于引用哪个对象


[招生]科锐逆向工程师培训(2026年11月13日实地,远程教学同时开班, 第57期)

收藏
点赞 ・39
打赏
分享
最新回复 (12)
雪    币: 238
活跃值: (621)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2 楼
66
21小时前
0
雪    币: 78
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3 楼
1111
20小时前
0
雪    币: 3269
活跃值: (6400)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4 楼
666
19小时前
0
雪    币: 410
活跃值: (1020)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5 楼
感谢分享
19小时前
0
雪    币: 227
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6 楼
感谢分享
18小时前
0
雪    币: 1901
活跃值: (6018)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
7 楼
111
18小时前
0
雪    币: 1207
活跃值: (1076)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
8 楼
iBa0 111
222
16小时前
0
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9 楼
6666
16小时前
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10 楼
666
15小时前
0
雪    币: 74
活跃值: (900)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11 楼
435
14小时前
0
雪    币: 4306
活跃值: (4275)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
12 楼
look
13小时前
0
雪    币: 114
活跃值: (850)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13 楼
666
13小时前
0
游客
登录 | 注册 方可回帖
返回