某鱼App逆向

请求参数分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
{
"app": "com.taobao.idlefish",
"duration": "1923ms",
"headers": {
"x-sgext": "...",
"umid": "...",
"x-sign": "...",
"x-magic_device": "0",
"x-nettype": "WIFI",
"x-pv": "6.3",
"x-nq": "WIFI",
"EagleEye-UserData": "...",
"first_open": "0",
"x-features": "27",
"channel_2": "",
"x-app-conf-v": "0",
"x-mini-wua": "...",
"content-type": "application/x-www-form-urlencoded;charset\u003dUTF-8",
"Content-Length": "1183",
"oaid": "...",
"x-t": "1767621947",
"Content-Type": "application/x-www-form-urlencoded;charset\u003dUTF-8",
"x-bx-version": "6.6.231201.33656539",
"f-refer": "mtop",
"x-nw-retry": "true",
"x-extdata": "openappkey%3DDEFAULT_AUTH",
"x-ttid": "231200%40fleamarket_android_7.24.50",
"x-app-ver": "7.24.50",
"x-c-traceid": "null17676219472770058131963",
"x-location": "0%2C0",
"x-umt": "...",
"a-orange-q": "appKey\u003d21407387\u0026appVersion\u003d7.24.50\u0026clientAppIndexVersion\u003d1120260105180700565\u0026clientVersionIndexVersion\u003d0",
"x-utdid": "...",
"c-launch-info": "3,0,1767621947276,1767621723839,3",
"x-screen-level": "small",
"x-appkey": "21407387",
"x-falco-id": "...",
"user-agent": "MTOPSDK%2F3.1.1.7+%28Android%3B10%3BGoogle%3BPixel+XL%29+DeviceType%28Phone%29",
"Host": "g-acs.m.goofish.com",
"Accept-Encoding": "gzip",
"Connection": "Keep-Alive"
},
"method": "POST",
"protocol": "HTTP/1.1",
"remoteIp": "203.119.252.50",
"remotePort": 443,
"sessionId": "23672b55-03e4-45a6-b937-a8463af65b16",
"time": "2026-01-05 22:05:49",
"url": "https://g-acs.m.goofish.com/gw/mtop.taobao.idlemtopsearch.search/1.0/"
}
字段 备注
x-sgext 变化
umid 固定
x-sign 变化
x-mini-wua 变化
oaid 固定
x-t 秒级时间戳
x-extdata openappkey%3DDEFAULT_AUTH 固定
x-ttid 231200%40fleamarket_android_7.24.50 固定
x-c-traceid null + 毫秒级时间戳 + 4位数字(当前请求次数?) +131963
x-location 经纬度
x-umt 固定
x-utdid 固定
c-launch-info 3,0,1767621947276,1767621723839,3 3,0,相比x-c-traceid更早的毫秒级时间戳, app启动还是安装的时间戳?
x-falco-id 变化
user-agent sdk+版本 + 设备信息

同一次搜索,请求前10个商品的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
"abConfigs": "{"isEnterResultNative":"true","isSearchResultNative":"true"}",
"activeSearch": false,
"apiName": "com.taobao.idlefish.search_implement.protocol.SearchResultReq",
"bizFrom": "home",
"disableHierarchicalSort": 0,
"extraFilterValue": "{"divisionList":[],"excludeMultiPlacesSellers":"0"}",
"forceUseInputKeyword": false,
"forceUseTppRepair": false,
"fromFilter": false,
"fromKits": false,
"fromLeaf": false,
"fromShade": false,
"fromSuggest": false,
"keyword": "bike",
"mainTab": true,
"originJson": false,
"page": 1,
"pageNumber": 1,
"relateResultListLastIndex": 0,
"relateResultPageNumber": 1,
"resultListLastIndex": 0,
"rowsPerPage": 10,
"searchReqFromActivatePagePart": "searchButton",
"searchReqFromPage": "xyHome",
"searchTabType": "SEARCH_TAB_MAIN",
"shadeBucketNum": "-1",
"smartUIFilter": true,
"suggestBucketNum": "29",
"supportFlexFilter": true
}

同一次搜索,下拉加载更多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"abConfigs": "{"isEnterResultNative":"true","isSearchResultNative":"true"}",
"activeSearch": false,
"apiName": "com.taobao.idlefish.search_implement.protocol.SearchResultReq",
"bizFrom": "home",
"disableHierarchicalSort": 0,
"extraFilterValue": "{"divisionList":[],"excludeMultiPlacesSellers":"0","hideExcludeMultiPlacesSellers":true}",
"forceUseInputKeyword": false,
"forceUseTppRepair": false,
"fromFilter": false,
"fromKits": false,
"fromLeaf": false,
"fromShade": false,
"fromSuggest": false,
"keyword": "bike",
"mainTab": true,
"originJson": false,
"page": 1,
"pageNumber": 2,
"relateResultListLastIndex": 0,
"relateResultPageNumber": 1,
"resultListLastIndex": 11,
"rowsPerPage": 10,
"searchReqFromActivatePagePart": "searchButton",
"searchReqFromPage": "xyHome",
"searchTabType": "SEARCH_TAB_MAIN",
"shadeBucketNum": "-1",
"singleControl": false,
"smartUIFilter": true,
"sqiControlFieldsJson": "{"abid":"477862","aiTipSelected":"false","allowMatchActivity":"true","allowUserTppRepairEmptyRes":"true","bproDocsFound":"0","canShowUserSubscribeTips":"false","cateIdList":["50023914","50023914","50025418","50025418","50025467","50025418","50025449","50112002","50025418","50023914"],"docNumWhenFirstPage":"480000","enableNewPubRank":"false","fetchItemLabelClient":"true","filterPersonal":"false","forceEraseSearchKeywords":"false","forceForbidAdItems":"false","forceNotBookInetnt":"false","forceOnlyCallYuXiaoPuOrPlayboyItems":"false","forceOnlyIdleCoinItems":"false","forceSrpFeedsEmpty":"false","forceYoupinItems":"false","fromMini":"false","fromNewPublishPage":"false","fromPc":"false","hasFilterWhenFirstPage":"false","hasMachRulesWhenFirstPage":"false","hasNewArrival":"false","hasSortWhenFirstPage":"false","hideExcludeMultiPlaces":"true","hitAdvanceSearchDelete":"false","hitBackgroundMaterial":"false","hitBasePanel":"false","hitChannelSourceFromNavMat":"false","hitFilterPersonalHousePanel":"false","hitFilterPersonalPanel":"false","hitGraphSearchSubscribeCpv":"false","hitLevel1Kfc":"false","hitLevel4Kfc":"false","hitLevel5Kfc":"false","hitLevel6Kfc":"false","hitLevelDealPriceKfc":"false","hitRelatedWordInterfereMat":"false","hitRigorousNewStylePanel":"false","hitSidelineV2":"false","hitToBuyPage":"false","hitl2l3SensitiveWordInterfereMat":"false","ignoreEmptyAlarm":"false","iosCouponsForbid":"false","itemIdList":["979733895150","990538260383","674309903803","918085444363","977728412088","988822438547","1003976314409","1007872356565","837081527647","999544611726"],"lessItem":"false","machDataBecauseAutoFailover":"false","materialFromAds":"false","moduleBucketMap":{},"myFollowChannelCallAllItemAutoFailover":"false","myFollowSearch":"false","objectTransferFromClient":"false","onePackSellEnabled":"false","openDesign":"true","personalChannelCallAllItemAutoFailover":"false","personalSearchShowRecommendQuery":"false","poiLocations":[],"poiType":"","privacyOptionEnabled":"false","recallCouponItems":"false","recallOutletsItem":"false","recallSkuStockItems":"false","recommendKeywords":["小八平衡车","8平衡车","平衡车8","元气100平衡车","8平衡车","元气100","单车","自行车","自行车山地","山地车自行车","自行车26","平衡车比赛","山地自行车","变速自行车","26寸自行车","皮带自行车儿童","cargo自行车","比赛平衡车","20寸自行车+铝合金"],"researchFlag":"false","researchQuery":"","searchFeedsDataFrom":"IDLE_ALLDOM_SQI_SRP_FEEDS_DATA_FROM_IDLESEARCH","showUserSubscribeButtonTip":"false","showUserSubscribeEmptyResTip":"false","showUserSubscribeLessResTip":"false","singleControlInner":"false","sourceScene":"main_search_empty_rec","sqiCloseAlimamaAd":"false","sqiCloseIdleAd":"false","supportRecallSubscribePriceReduce":"false","tppDataBecauseAutoFailover":"false","unBlockItemPool":[],"userInputOriginalSearchKeywords":"bike"}",
"suggestBucketNum": "29",
"supportFlexFilter": true
}

搜索请求RPC

请求与响应的hook 点定位

根据同一关键字的多次请求,可以发现resultListLastIndex字段的值发生改变,以及增加了sqiControlFieldsJson字段。jadx中搜索这两个关键字

定位关键方法resetPageNumber以及updatePageNumberAndIndex方法。查看该方法的用例,定位到refresh以及loadMore方法。

根据方法名以及代码逻辑,可以确定refreshloadMore方法首先更新参数(刚才所提及的字段),然后调用requestMtop方法,该方法对searchResultReq填充字段,然后发送请求,主要代码部分如下

请求成功调用onSuccess方法,该方法调用access$100方法

ThreadUtils.runOnUI方法顾名思义就是通过线程更新搜索结果展示的UI界面,trackRefreshAndMore方法则可能是日志记录。

很显然,我们可以利用这个hook点从searchResultResp对象中获取到响应结果。至于SearchResultResp中各个字段的含义,可以与响应结果进行对比,很容易知道这些字段都是什么内容。

通过对比,知道了商品信息在resultList中。

响应结果获取的hook 点找到了,接下来就是找请求构造

hook refreshloadMore方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function main() {
Java.perform(
function () {
let SearchResultModel = Java.use("com.taobao.idlefish.search_implement.mvp.model.SearchResultModel");
// 首次搜索
SearchResultModel["refresh"].implementation = function (onMtopCallback) {
console.log(`SearchResultModel.refresh is called: onMtopCallback=${onMtopCallback.$className}`);
this["refresh"](onMtopCallback);
};
// 加载更多
SearchResultModel["loadMore"].implementation = function (z, onMtopCallback) {
console.log(`SearchResultModel.loadMore is called: z=${z}, onMtopCallback=${onMtopCallback.$className}`);
this["loadMore"](z, onMtopCallback);
};
}
)
}
setImmediate(main);

根据测试,可以确认refresh是重新加载与搜索关键字相关的商品,loadMore是加载更多与搜索关键字相关的商品。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SearchResultModel.refresh is called: onMtopCallback=com.taobao.idlefish.search_implement.mvp.presenter.SearchResultPresenter$1
java.lang.Exception
at com.taobao.idlefish.search_implement.mvp.model.SearchResultModel.refresh(Native Method)
at com.taobao.idlefish.search_implement.mvp.presenter.SearchResultPresenter.refreshAll(SearchResultPresenter.java:26)
at com.taobao.idlefish.search_implement.SearchResultActivity.onCreate(SearchResultActivity.java:39)
...
SearchResultModel.loadMore is called: z=true, onMtopCallback=com.taobao.idlefish.search_implement.mvp.presenter.SearchResultPresenter$1
java.lang.Exception
at com.taobao.idlefish.search_implement.mvp.model.SearchResultModel.loadMore(SearchResultModel.java:9)
at com.taobao.idlefish.search_implement.mvp.presenter.SearchResultPresenter.loadMore(SearchResultPresenter.java:46)
at com.taobao.idlefish.search_implement.SearchResultActivity.lambda$onResultViewCreated$3(SearchResultActivity.java:3)
at com.taobao.idlefish.search_implement.SearchResultActivity.$r8$lambda$-q-edhOrb4rz1ZyPRMmkJ54y4-A(SearchResultActivity.java:1)
at com.taobao.idlefish.search_implement.SearchResultActivity$$ExternalSyntheticLambda0.onLoadMore(R8$$SyntheticClass:3)
at com.taobao.idlefish.xcontainer.view.delegate.ResultViewDelegate.$r8$lambda$ZcKRHTJ9-nlJiYKsFct2PK7btew(ResultViewDelegate.java:5)
at com.taobao.idlefish.xcontainer.view.delegate.ResultViewDelegate$$ExternalSyntheticLambda0.onLoadMore(R8$$SyntheticClass:3)
at com.taobao.idlefish.xcontainer.listener.recyclerview.OnScrollStateListener.loadMore(OnScrollStateListener.java:27)
at com.taobao.idlefish.xcontainer.listener.recyclerview.OnScrollStateListener.onScrolled(OnScrollStateListener.java:197)
...

它们的回调函数都是com.taobao.idlefish.search_implement.mvp.presenter.SearchResultPresenter$1匿名函数。

通过堆栈可以发现,refreshloadMore方法分别由SearchResultPresenter类中的refreshAllloadMore方法调用而来的。再往前溯源,则是SearchResultActivity类中的方法了,很显然,这是搜索结果展示页面。它对应的onCreate()方法如下

其中initRequestParams顾名思义就是初始化请求参数,这里很可能就有我们的搜索关键字。

frida hook该方法,打印routerParams字段

1
2
3
4
5
6
7
8
9
10
11
12
function main() {
Java.perform(
function () {
let SearchResultActivity = Java.use("com.taobao.idlefish.search_implement.SearchResultActivity");
SearchResultActivity["initRequestParams"].implementation = function () {
this["onCreate"](bundle);
console.log("routerParams: " + this.routerParams.value);
};
}
)
}
setImmediate(main);

结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
routerParams: {
"_from__": "searchresult",
"animated": "false",
"bizFrom": "home",
"extraFilterValue": {
"divisionList": [],
"excludeMultiPlacesSellers": "0"
},
"flutter_animated": "false",
"hide_splash_screen": "true",
"initSubPageTag": "searchResult",
"isFadeIn": "false",
"isOldFriendly": "false",
"keyword": "phone",
"kitSearchOnce": "false",
"searchReqFromActivatePagePart": "searchButton",
"searchReqFromPage": "xyHome",
"searchTabType": "SEARCH_TAB_MAIN",
"shadeBucketNum": "-1",
"suggestBucketNum": "29"
}

此时我们的搜索关键字(”phone”)已经存在了。

梳理一下搜索功能的代码逻辑,首先会通过SearchResultActivity.onCreate()方法创建搜索结果展示页面,该方法里面会填充基本的请求参数(此时还未生成x-系列),创建一个searchResultPresenter实例,然后调用refreshAll()方法进行首次网络请求,该方法进一步调用SearchResultModel.refresh()方法,设置页码以及sqicontrol字段的值,最终通过requestMtop()方法完成请求的最后组装,最终发送请求。如果是加载更多商品,则是调用SearchResultPresenter.loadMore()方法,后续步骤类似,不在讲述。

RPC制作

通过内存搜索SearchResultActivitySearchResultPresenterSearchResultModel三个实例,发现只有在搜索页面出现的情况下才在内存中存在唯一实例,因此可以借助这些实例来完成自己的任意关键字搜索。

思路如下:

选取的hook点为searchResultModelrefreshloadMore方法,因为符合功能最小化原则(防止后续调用过多无关函数),且这两个hook点会自动处理参数生成(如x-系列),以及自动根据响应结果更新请求(updatePageNumberAndIndexupdateSqiControlFieldsJson)。

最主要是搜索关键字的修改,需要注意的是修改routerParams无效!因为routerParams虽然在requestMtop方法中存在使用,但并没有修改requestkeyword,因此我们需要直接获取request实例然后修改keyword字段。最后需在access$100处解析响应结果,但是不要调用原方法,以防止回调函数触发导致一系列可能的报错。

rpc脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
const Cached = {
searchResultPresenter: null,
searchResultModel: null,
keyword: null,
pendingResolve: null // 新增:用于将数据同步回传给 RPC 调用者
}

// 1. 预先获取类引用,避免在 RPC 中重复查找
const Classes = {
SearchResultPresenter: null,
SearchResultModel: null,
AnonymousClass1: null,
JSON: null,
SearchResultResp: null
};

function init() {
Java.perform(() => {
// 缓存类引用
Classes.SearchResultPresenter = Java.use("com.taobao.idlefish.search_implement.mvp.presenter.SearchResultPresenter");
Classes.SearchResultModel = Java.use("com.taobao.idlefish.search_implement.mvp.model.SearchResultModel");
Classes.AnonymousClass1 = Java.use("com.taobao.idlefish.search_implement.mvp.presenter.SearchResultPresenter$1");
Classes.JSON = Java.use("com.alibaba.fastjson.JSON");
Classes.SearchResultResp = Java.use("com.taobao.idlefish.search_implement.protocol.SearchResultResp");
// 初始获取 SearchResultPresenter 实例
Java.choose(Classes.SearchResultPresenter.$className, {
onMatch: (instance) => {
Cached.searchResultPresenter = instance;
console.log("[+] 找到 SearchResultPresenter 实例: " + instance);
},
onComplete: () => {
throw new Error("未找到 SearchResultPresenter 实例。请将闲鱼App置于搜索结果页面后重试。");
}
});

Cached.searchResultModel = Java.cast(Cached.searchResultPresenter.getModel(), Classes.SearchResultModel);
// hook access$100 方法以捕获响应
Classes.SearchResultModel["access$100"].implementation = function (model, i, req, resp, callback) {
const jsonString = Classes.JSON.toJSONString(Java.cast(resp, Classes.SearchResultResp));
// A. 如果有 RPC 正在等待,直接通过 Promise 返回
if (Cached.pendingResolve) {
Cached.pendingResolve(jsonString);
Cached.pendingResolve = null;
}
};

console.log("[*] Hooks and Classes initialized.");
});
}

function search(keyword) {
// 必须返回 Promise,否则 Python 端无法通过 rpc 接收到 resolve 的数据
return new Promise((resolve) => {
// 将当前 resolve 句柄存入全局,供 access$100 异步触发
Cached.pendingResolve = resolve;

Java.perform(() => {
const callback = Classes.AnonymousClass1.$new(Cached.searchResultPresenter);
if (Cached.keyword === null || Cached.keyword !== keyword) {
// 修改关键词并执行刷新
let req = Cached.searchResultModel.request.value;
req.keyword.value = keyword;

console.log("[RPC] 执行刷新: " + keyword);
Cached.searchResultModel.refresh(callback);

Cached.keyword = keyword;
} else {
// 关键词相同,执行加载更多
console.log("[RPC] 执行加载更多");
Cached.searchResultModel.loadMore(true, callback);
}
});
});
}

// 初始化
init();

// 4. 定义 RPC 导出
rpc.exports = {
// 统一触发方法
search: search
};

python端代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import frida
import json

def main(pid):

session = frida.get_usb_device().attach(pid)

with open("xianyurpc.js", "r", encoding="utf-8") as f:
script_content = f.read()

script = session.create_script(script_content)
script.load()

# ———————示例——————
# 第一次搜关键词,触发 refresh
print("正在搜索自行车...")
res_json = script.exports_sync.search("自行车")
print("搜索结果详情:", res_json[:100] + "...") # 打印前100个字符

# 第二次搜相同关键词,触发 loadMore
print("加载更多结果...")
more_json = script.exports_sync.search("自行车")
print("更多结果详情:", more_json[:100] + "...")

if __name__ == "__main__":
main(23686)

以及导出成服务端API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import frida
import uvicorn
from fastapi import FastAPI, Query, HTTPException
from contextlib import asynccontextmanager
import json

# --- 全局变量管理 ---
class FridaManager:
def __init__(self):
self.session = None
self.script = None
self.device = None

def init_frida(self, package_name="闲鱼"): # "com.taobao.idlefish"
try:
self.device = frida.get_usb_device()
self.session = self.device.attach(package_name)
with open("xianyurpc.js", "r", encoding="utf-8") as f:
self.script = self.session.create_script(f.read())

self.script.load()
print("Successfully attached to Idlefish and loaded script.")
except Exception as e:
print(f"Failed to init Frida: {e}")

manager = FridaManager()

# 使用 FastAPI 的生命周期管理
@asynccontextmanager
async def lifespan(app: FastAPI):
manager.init_frida()
yield
if manager.session:
manager.session.detach()

app = FastAPI(lifespan=lifespan)

# --- API 路由 ---
@app.get("/search")
async def api_search(q: str = Query(..., description="搜索关键词")):
"""
HTTP 接口:搜索商品
q: 关键词
"""
if not manager.script:
raise HTTPException(status_code=500, detail="Frida script not loaded")

try:
# 调用 Frida RPC
# exports_sync 是阻塞的,直到 JS 里的 resolve 被调用
result_json = manager.script.exports_sync.search(q)

# 将字符串转为 JSON 对象返回给 Web 客户端
return json.loads(result_json)

except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

if __name__ == "__main__":
# 启动 Web 服务
uvicorn.run(app, host="0.0.0.0", port=8000)

网络访问/search API的果如下

x-系列参数生成定位

native层定位

x-sgext为例, hook HashMap过滤x-sgext并打印函数调用堆栈

1
2
3
4
5
6
7
8
9
10
11
12
13
function main() {
Java.perform(function () {
var hashMap = Java.use("java.util.HashMap");
hashMap.put.implementation = function (a, b) {
if (a != null && a.equals("x-sgext")) {
console.log("Hooked x-sgext header: " + b);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
}
return this.put(a, b);
}
})
}
setImmediate(main);

结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
hooked x-sgext header: JBKuXHhu9np+9obhAvi89FyfbJ9lnH+ca51lnX+cbI1/n2uYa5Zpnm2cbo1snGjPbJ5sn23PacpkzWqWf59qjW2cf5xsjWyebJl/mH+ff59/n3+ff59/n3+ff59/n3+ef55/nn+ef55/nn+ef405jWyNbI04nWTNbJx/nmyebJ5/nn+cOM9/nn+Nb41tjWyNb40m7w/Wf55/jnyfbI58n2+NaPFuiGqXepx6nnqXep56nnqXep96nnqeep96n2+dbZ5riA7EM/Fp8WyIbIhql2nIaJ49mXqeep56nnqeep56ngPPA5x6nnqeA59s8Q7/GfQT4B3BOeEa7x3sJM0a73eePfcX72zgc98VxhXvDp8GyRHsBO8d7AzjMvkVzy/ZGoEX6w3tH+s2yRbJF+8D
java.lang.Exception
at java.util.HashMap.put(Native Method)
at com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method)
at com.alibaba.wireless.security.mainplugin.doCommand(Unknown Source:0)
at com.alibaba.wireless.security.middletierplugin.d.d.a.a(Unknown Source:370)
at com.alibaba.wireless.security.middletierplugin.d.d.a$a.invoke(Unknown Source:56)
at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
at $Proxy29.getSecurityFactors(Unknown Source)
at mtopsdk.security.InnerSignImpl.getUnifiedSign(InnerSignImpl.java:299)
at mtopsdk.mtop.protocol.builder.impl.InnerProtocolParamBuilderImpl.buildParams(InnerProtocolParamBuilderImpl.java:853)
at mtopsdk.framework.filter.before.ProtocolParamBuilderBeforeFilter.doBefore(ProtocolParamBuilderBeforeFilter.java:6)
...

com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative在jadx中搜索不到,显然这个类是动态加载的(通过.jar.dex形式动态加载)。使用 frida 来枚举一下 classloader 来定位这个类在哪个文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function findTargetClassWhere() {
Java.perform(function () {
var targetClass = "com.taobao.wireless.security.adapter.JNICLibrary";
var found = false;
// 枚举所有的 ClassLoader
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
// 尝试在当前 loader 中加载目标类
if (loader.findClass(targetClass)) {
console.log("[+] 找到目标类: " + targetClass);
console.log("[+] 所在的 ClassLoader: " + loader);
found = true;
}
} catch (e) {}
},
onComplete: function () {
if (!found) {
console.log("[-] 在所有 ClassLoader 中均未找到该类。");
} else {
console.log("[*] 搜索完毕。");
}
}
});
})
}

setImmediate(findTargetClassWhere);

结果如下:

1
2
3
[+] 找到目标类: com.taobao.wireless.security.adapter.JNICLibrary
[+] 所在的 ClassLoader: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libsgmain.so"],nativeLibraryDirectories=[/data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64, /data/user/0/com.taobao.idlefish/app_SGLib/app_1767620786/main, /system/lib64, /vendor/lib64, /system/product/lib64]]]
[*] 搜索完毕。

classloader里的第一个文件就是我们要找的,名为libsgmain.so。看似是so文件,其实是jar文件,将其从对应文件夹中提取出来,放入jadx中进行分析。

接下来就定位doCommandNative函数在哪个so库中,由于它可能是静态加载也可能是动态加载,因此我们要 hook JNI 的 RegisterNatives 函数以及 libdl.so 的 dlsym 函数,代码如下(通过spawn方式启动):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
function FindNativeMethod(){
//寻找动态注册函数
// find RegisterNatives
let addrRegisterNatives = null;
var symbols = Process.findModuleByName("libart.so").enumerateSymbols();
for (var i = 0; i < symbols.length; i++){
var symbolName = symbols[i].name;
if (symbolName.indexOf("CheckJNI") == -1 && symbolName.indexOf("JNI") >= 0){
if(symbolName.indexOf("RegisterNatives") >= 0){
addrRegisterNatives = symbols[i].address;
}
}
}

console.log("[*] RegisterNatives address: " + addrRegisterNatives);

Interceptor.attach(addrRegisterNatives, {
onEnter: function(args){
var env = args[0];
var className = Java.vm.tryGetEnv().getClassName(args[1]);
var methods = args[2];
var methodCount = args[3].toInt32();
if (className.indexOf("JNICLibrary") != -1){
console.log("[*] ================== Class: " + className + " ======================");
for(var i = 0; i < methodCount; i++){
var idx = i * Process.pointerSize * 3;
var methodName = methods.add(idx).readPointer().readCString();
var methodSign = methods.add(idx + Process.pointerSize).readPointer().readCString();
var methodAddr = methods.add(idx + 2 * Process.pointerSize).readPointer();
var module = Process.findModuleByAddress(methodAddr);
console.log("[*] methodName: " + methodName + ", methodSignature: " + methodSign + ", methodAddress: " + methodAddr, ", offset: " + methodAddr.sub(module.base));
console.log("[*] moduleName: " + module.name + ", moduleAddress: " + module.base);
}
}
}
})

//寻找静态注册函数
let dlsymFuncAddr = Module.findExportByName('libdl.so', 'dlsym');
Interceptor.attach(dlsymFuncAddr, {
onEnter: function (args) {
this.libAddr = ptr(args[0]); // args[0]是库基址
this.funcName = ptr(args[1]); // args[1]是函数名
},
onLeave: function (retval) {
let module = Process.findModuleByAddress(retval);
if (this.funcName.readCString().indexOf("doCommandNative") != -1){
console.log("function name: ", this.funcName.readCString(),
"lib name: ", module.name,
"function's memory address: ", retval,
"function offset: ", retval.sub(module.base)
);
}
},
})
}

打印如下

1
2
3
[*] ================== Class: com.taobao.wireless.security.adapter.JNICLibrary ======================
[*] methodName: doCommandNative, methodSignature: (I[Ljava/lang/Object;)Ljava/lang/Object;, methodAddress: 0x7d103e90c4 , offset: 0x280c4
[*] moduleName: libsgmainso-6.6.231201.33656539.so, moduleAddress: 0x7d103c1000

ida分析该函数 0x280c4 ,发现都是BR间接跳转。

间接跳转去混淆

对应BR间接跳转混淆的修复,可以借助frida stalker将doCommandNative的指令轨迹记录下来,同时记录br\blr指令的跳转地址。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
function traceInsns(moduleName, traceOffset) {
const module = Process.findModuleByName(moduleName);
if (!module) {
console.error("Module not found: " + moduleName);
return;
}
console.log("[+] Found module: " + moduleName + " at " + module.base);

let file = null;
let logPath = "";
Java.perform(() => {
const context = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext();
// 获取 /data/user/0/com.taobao.idlefish/files 目录
const filesDir = context.getFilesDir().getAbsolutePath();
logPath = filesDir + `/${moduleName}_${traceOffset}.txt`;
file = new File(logPath, "w");
console.log("[+] 日志初始化成功: " + logPath);
});

// 定义追踪范围
const moduleStart = module.base;
const moduleEnd = module.base.add(module.size);

const traceStart = moduleStart.add(traceOffset);

// 全局锁:确保只跟踪一个全流程
let isTracking = false;
let targetThreadId = -1;
let insn_count = 0;

Interceptor.attach(traceStart, {
onEnter: function (args) {
const cmdId = args[2].toInt32(); // cmdId
if (cmdId != 70102 || isTracking) {//由于doCommandNative多次触发,因此限制仅记录一次
return;
}
console.log("start tracing instructions for cmdId 70102...");
isTracking = true;
targetThreadId = Process.getCurrentThreadId();
Stalker.follow(targetThreadId, {
transform: function (iterator) {
let instruction = iterator.next();
while (instruction !== null) {
const address = instruction.address;

// 判断是否是目标模块内的指令
if (address.compare(moduleStart) >= 0 && address.compare(moduleEnd) <= 0) {
// 解析当前指令涉及的寄存器
const insn_str = instruction.toString();
// 针对 br 以及 blr 指令,解析它使用的寄存器名
let targetReg = null;
if (instruction.mnemonic === 'br' || instruction.mnemonic === 'blr') {
const ops = instruction.operands;
if (ops.length > 0 && ops[0].type === 'reg') {
let r = String(ops[0].value);
// 统一转为 xN 格式适配 context
targetReg = r.startsWith('w') ? 'x' + r.substring(1) : r;
}
}
// 插入 Callout
iterator.putCallout(function (context) {
let logLine = `[${moduleName} ${address.sub(moduleStart)}]${address}: "${insn_str}"`;
// 如果是 br 指令,额外补充寄存器的值
if (targetReg !== null) {
const regValue = context[targetReg];
logLine += " ==> " + targetReg + " = " + String(regValue.sub(moduleStart));
}
// console.log(logLine);
file.write(logLine + "\n");
insn_count += 1;
if (insn_count % 1000000 === 0) {
console.log(`traced ${insn_count} instructions...`);
}
});
}
iterator.keep();
instruction = iterator.next();
}
}
});
},
onLeave: function (retval) {
if (isTracking && Process.getCurrentThreadId() === targetThreadId) {
Stalker.unfollow(targetThreadId);
Stalker.flush();
file.flush();
console.log("trace finished!");
}
}
});
}

function main() {
const moduleName = "libsgmainso-6.6.231201.33656539.so";
const traceOffset = 0x280c4;
traceInsns(moduleName, traceOffset);
}

setImmediate(main);

然后再从日志中提取br\blr指令中的地址,对于固定跳转地址的指令,进行间接跳转指令的修复。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import re
from collections import defaultdict

def parse_trace(file_path):
# 正则:捕获[指令相对偏移]和[br跳转的目标值]
pattern = re.compile(r"0x([0-9a-fA-F]+)\].*br\s+\w+\" ==> \w+\s+=\s+0x([0-9a-fA-F]+)")
# 存储格式: {指令地址: set(跳转目标1, 跳转目标2...)}
br_map = defaultdict(set)

with open(file_path, 'r', encoding='utf-8') as f:
for line in f:
match = pattern.search(line)
if match:
insn_addr = match.group(1)
target_addr = match.group(2)
br_map[insn_addr].add(target_addr)

print(br_map)
print("--- 解析结果 (指令相对地址, 跳转相对地址) ---")
fixed_jumps = []

for insn_addr in sorted(br_map.keys(), key=lambda x: int(x, 16)):
targets = list(br_map[insn_addr])
# 挑选出跳转地址固定的(即该指令地址对应的跳转目标只有一个)
if len(targets) == 1:
record = (eval(f"0x{insn_addr}"), eval(f"0x{targets[0]}"))
if record[1] < 0x100000000:
fixed_jumps.append(record)
print(f"{hex(record[0])} -> {hex(record[1])} [固定]")
else:
print(f"0x{insn_addr} -> {targets} [动态/多变]")

return fixed_jumps

fixed_results = parse_trace('E:\LearningLibrary\FridaHook/frida-agent-example/frida-agent-example-main/agent\stalker_trace\libsgmainso.txt')
print(fixed_results)

修复脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import struct


def patch_so_file(input_path, output_path, patch_list):
# 读取原始文件
with open(input_path, 'rb') as f:
data = bytearray(f.read())

count = 0
for insn_off, target_off in patch_list:
try:
# 1. 计算 AArch64 B 指令的偏移量
# 公式: (target_addr - pc_addr) / 4
diff = target_off - insn_off

if diff % 4 != 0:
print(f"[-] 跳过未对齐地址: {hex(insn_off)}")
continue

# 2. 构造 B 指令机器码
# B 指令高 6 位是 0x14 (000101),低 26 位是 imm26
imm26 = (diff // 4) & 0x03FFFFFF
opcode = 0x14000000 | imm26

# 3. 转为小端序字节 (AArch64 常用)
patch_bytes = struct.pack("<I", opcode)

# 4. 物理替换字节
# 这里的 insn_off 必须是文件偏移 (File Offset)
data[insn_off:insn_off + 4] = patch_bytes
count += 1
# print(f"[+] Patched {hex(insn_off)} -> B {hex(target_off)}")

except Exception as e:
print(f"[-] 修复失败 {hex(insn_off)}: {e}")

# 保存新文件
with open(output_path, 'wb') as f:
f.write(data)

print(f"\n[***] 修补完成!")
print(f"[***] 原始文件: {input_path}")
print(f"[***] 修复文件: {output_path}")
print(f"[***] 共计修补处数: {count}")

input_so = "libsgmainso-6.6.231201.33656539.so" # 你的原始文件名
output_so = "libsgmain_patched.so" # 修复后的文件名
patch_list =[(154608, 154632), (154900, 154912), (155060, 155084), (155080, 155084), (155152, 155156), (155484, 155244), (155572, 155596), (164172, 164196), (173844, 173868), (174064, 174136), (174224, 174296), (299920, 299944), (360316, 360344), (360860, 360864), (436780, 436804), (437012, 437016), (437040, 437044), (437144, 437168), (437352, 437356), (437384, 437388), (437428, 437432), (437548, 437552), (437704, 437708), (444032, 444056), (444188, 444208), (459584, 459612), (459904, 459932), (895916, 895936), (901408, 901432), (901592, 901604), (902536, 902564), (903984, 903988), (904260, 904264), (904288, 904300), (904420, 904432), (905024, 905048), (908824, 908852), (909948, 909972), (911608, 911632), (1011988, 1012012), (1055700, 1055724), (1225092, 1225096), (1226152, 1226172), (1226372, 1226396), (1230000, 1230028), (1230980, 1231004), (1231084, 1231112), (1231476, 1231496), (1232652, 1232676), (1232884, 1232912), (1243872, 1243896), (1255964, 1256056), (1256456, 1256056), (1256512, 1256056), (1256588, 1256056), (1257016, 1256788), (1257060, 1256788), (1257748, 1258196), (1258748, 1258028), (1259644, 1257108), (1259696, 1256700), (1259852, 1258404), (1260112, 1257616), (1260424, 1258620), (1260476, 1258404), (1260528, 1257108), (1260920, 1256700), (1261448, 1258620)]

patch_so_file(input_so, output_so, patch_list)

然后再借助AI修复,最终展示效果如下:

!

其中sub_25B78是根据cmdId选取待执行的函数,sub_109B94则是抛SecException的地方。

参数7012的确定

尝试打印doCommandNative的入参,发现该函数一直被调用,因此需要通过第一个参数进行过滤。如何寻找第一个参数呢,可以根据doCommandNative的调用堆栈

1
2
3
4
5
6
7
hooked x-sgext header: JBKuXHhu9np+9obhAvi89FyfbJ9lnH+ca51lnX+cbI1/n2uYa5Zpnm2cbo1snGjPbJ5sn23PacpkzWqWf59qjW2cf5xsjWyebJl/mH+ff59/n3+ff59/n3+ff59/n3+ef55/nn+ef55/nn+ef405jWyNbI04nWTNbJx/nmyebJ5/nn+cOM9/nn+Nb41tjWyNb40m7w/Wf55/jnyfbI58n2+NaPFuiGqXepx6nnqXep56nnqXep96nnqeep96n2+dbZ5riA7EM/Fp8WyIbIhql2nIaJ49mXqeep56nnqeep56ngPPA5x6nnqeA59s8Q7/GfQT4B3BOeEa7x3sJM0a73eePfcX72zgc98VxhXvDp8GyRHsBO8d7AzjMvkVzy/ZGoEX6w3tH+s2yRbJF+8D
java.lang.Exception
at java.util.HashMap.put(Native Method)
at com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method)
at com.alibaba.wireless.security.mainplugin.doCommand(Unknown Source:0)
at com.alibaba.wireless.security.middletierplugin.d.d.a.a(Unknown Source:370)
at com.alibaba.wireless.security.middletierplugin.d.d.a$a.invoke(Unknown Source:56)

com.alibaba.wireless.security.middletierplugin.d.d.a.a方法也不在apk中,通过定位确定在

1
2
3
[+] 找到目标类: com.alibaba.wireless.security.middletierplugin.d.d.a
[+] 所在的 ClassLoader: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libsgmiddletier.so"],nativeLibraryDirectories=[/data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64, /data/user/0/com.taobao.idlefish/app_SGLib/app_1767620786/main, /data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64, /system/product/lib64]]]
[*] 搜索完毕。

libsgmiddletier.so其实是个.jar文件,对应方法如下

可以确认第一个参数位70102

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function HookJavaDoCommandNative() {
Java.perform(function () {
var targetClass = "com.taobao.wireless.security.adapter.JNICLibrary";
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if (loader.findClass(targetClass)) {
console.log("[+] Find Loader: " + loader);
Java.classFactory.loader = loader;
var JNICLibrary = Java.classFactory.use(targetClass);
JNICLibrary["doCommandNative"].implementation = function (i, objArr) {
var result = this.doCommandNative(i, objArr);
//objectArr 可能是一个复杂对象,直接打印可能导致闪退
if (i == 70102) {
console.log(`>>> doCommandNative is called: i=${i}, objArr=${objArr}`);
console.log(`<<< doCommandNative result=${result}`);
}
return result;
};
}
} catch (e) {}
},
onComplete: function () {
console.log("[*] 扫描结束");
}
});
});
}

结果如下

1
2
3
4
5
6
>>> doCommandNative is called: i=70102, objArr=21407387,aVvA4IboYJQDAPf8FcEC0UhS&&&21407387&a0fe5c4111c15cc20e9c7d1e435d1e4a&1767857182&mtop.taobao.idlemtopsearch.search&1.0&&231200@fleamarket_android_7.24.50&&0&0&openappkey=DEFAULT_AUTH&27&&&&&&&,false,0,mtop.taobao.idlemtopsearch.search,pageId=&pageName=,,,,r_43,0,0
<<< doCommandNative result={x-sgext=JBIPmr7PMNu4V0BAxFl6VZo+qj6jPbk9oj6uObk7qSy5Pq05rTevO6I6oiyqPa5uqj+qPqtur2uibKw3uTytLKs9uT2qLKo/qji5Obk+uT65Prk+uT65Prk+uTu5PLk/uT+5P7k/uT+5P7k/uSz/LKosqiz+PKJsqDm5P6o/qj+5P7k9/m65P7ksqSyrLKosqSzgTsl3uT+5L7o9ry+6PfssrlCoKaw2vD+8P7w9+ymoKagpqimpKagpqCmqKas6qDmrObxGznzFOsU/vD+8OaM6/Dr5Pq0pqimqKaopqimoKapQ+1CoKaopqlCrP8Vdy0rARNRO9UDLSctM2HffINskqm7LRNs/1SD1SvJH212oVf1B2GHeIOJD12HDRvt87Um1RN9e2EzYZfNFw0TbUA==, x-umt=jAEBTQNLPJdxbAKbl3be2iObOyFNWQa4, x-mini-wua=apgQTv3ngf3j/rMtgf1ZEbk7ReAf+JR1ZAM+LqnVhu5pGFyL/rrcRDeEjxsx5C7l7Il1tIELPiH8nVU/6Pmab/sHcKlIK4Kl7XK89DkYj6H541kQtyAAYwoy4nPBpBDhg31Aw912N9ptRbI9+T/JPWqI7BA06ISJKTOsYAEUbQFRywhuvUlzd5zLhXLHaBUBljPA=, x-sign=azU7Bc002xAAJUfgcnMKiJ0rEvZ1VUflSUlCU9BHvC1fJVfxl9H0LDDzh8z+kioI5dz0V1Mw147Ev1ORFKQLUcaBwXVX5UflR8VH5U}
>>> doCommandNative is called: i=70102, objArr=21407387,aVvA4IboYJQDAPf8FcEC0UhS&&&21407387&022bc7b91aed2ac0e3f1d14f6d006ee5&1767857182&mtop.taobao.idle.user.strategy.list&1.0&&231200@fleamarket_android_7.24.50&&0&0&openappkey=DEFAULT_AUTH&27&&&&&&&,true,0,mtop.taobao.idle.user.strategy.list,pageId=&pageName=,,,,r_44,0,0
<<< doCommandNative result={x-sgext=JBKVKg5VgEEIzfDadMPKzyqkGqQTpwmnEqQeowmhHrYJpB2jHa0foRKgErYapx70GqUapBv0H/ES9hytCaYSthunCacathqlGqIJowmkCaQJpAmkCaQJpAmkCaEJpgmlCaUJpQmlCaUJpQmlCbZPthq2GrZOphL2GKMJpRqlGqUJpQmnTvQJpQm2GbYbthq2GbZQ1HntCaUJtQqnHLUKp0u2HsoYsxysDKUMowynSLMZsxmzGrMZsxizGLMasxugGKMbowzcfuZ1oHWlDKUMoxOgTKBJpE+zGrMasxqzGrMZsxvKS8oYsxqzGsobpXXHe9Bw3mTURdp703vWaO1vumu+GvR73mulZbpF0ELda8cYz03baPtuulLZZ/tz3EvmXdMF3m/EaNZo/0Pfc95ryg==, wua=gLpH_erlTs5FUBPOWxC1vj2YWpGJ1HxKPFDCu+mjzr1dhvwCdrN/aXUrIPOswGiA8P1l6JiMd89WUvOTD7UjQuJm4H1+lO+y3QNMtl+IYQYj5KuBTgJ7CiSznF+qsg82U9qdLIM1u2GzYekpt1k/EorPIqh2P/gMy+3mCBk6P6wyVakyuEFUJr74WwuJyRHLeJO2bQeLikzrrS+/OLi06lPs4P0J9YtyDNP7JVYQHjpYJdg5uWgGBwgoHCzK9r2aVMXSKh4s39iVifDszxWnq7RWg+94CN5y7DqZdMMa/NtllTbD0UIgB35Cygq5hJ+spi1vsDSFO7SohEpMzR1nbAUB6N0mRtH7uTa7gO4Seu2O7KfE=, x-umt=jAEBTQNLPJdxbAKbl3be2iObOyFNWQa4, x-mini-wua=anwSMrS9rlDoyt8vwRcDpkQdKvyeQbjyi8c1APvzU6PB/bBjb/8cB8b6ixqHseaZnDx1o4/K9S2wgthmMNcWHTx5+yFA23ZuMBj+Fwmf2JjIsEm2GLcAIMXdSaP6+CD3y17fx9ozyqFtcwsCO0zNokfgzrkPvKcNUjlbs9K2os6i3qPnCmo5JhhuMDBpfUw+EgaI=, x-sign=azU7Bc002xAAIAnYRzvq5uz+fQg7EAnQB3wMZp5y8hgREBnE2eS6GX7Gyfmwp2Q9q+m6Yh0FmbuKih2kWpFFZIi0j0JJ0AnQCeAJ0A}
>>> doCommandNative is called: i=70102, objArr=21407387,aVvA4IboYJQDAPf8FcEC0UhS&&&21407387&ecc151258314647b68b5710c0ac85fe7&1767857343&mtop.idle.ad.expose&1.0&&231200@fleamarket_android_7.24.50&&0&0&openappkey=DEFAULT_AUTH&27&&&&&&&,false,0,mtop.idle.ad.expose,pageId=&pageName=,,,,r_45,0,0
<<< doCommandNative result={x-sgext=JBKTweVTa0fjyxvcn8UhycGi8aL4oeKh+aL1peKn9LDioval9qv0p/mm+bDxofXy8aPxovDy9Pf58Per4qD4sPCh4qHxsPGj8aTipeKi4qLiouKi4qLiouKi4qbioOKj4qPio+Kj4qPio+Kj4rCksPGw8bCloPnw86Xio/Gj8aPio+KhpfLio+Kw8rDwsPGw8rC70pLr4qPis+Gh9rPhoaKw9czztfeq56Pno+ehorX1tfW18bX1tfK18rXxtfCm86XzoOerheCepp6j56Pnpfimp6aioqS18bXxtfG18bX1tfDMoMzztfC18czwo57BkNab1I/SpvKO1YDQgsGM0oC48fKY2rajj7yw8IPDpsHwyabfg/2FvLndjP2S2qDgttXu2ITCg9CD+aDZpt6AzA==, x-umt=jAEBTQNLPJdxbAKbl3be2iObOyFNWQa4, x-mini-wua=azQTqjvZEAEx3f6eCKyWSxSd5KxzaRcQ5H58Q6tME10xt8RDbABgbrVx9z+k8pl2LxpjAxLyeJhZVLEcrBO3tns18ETrZis8IPGoXcDoAB5FRoy4T4tcNn8cCaoxf+Ob0Vv9PXz4S2pm9zB3GSIV4EaJaTLMbGvSK9Py7ym4x2yyqCnyUsoEp/BKclE73cM8TK/c=, x-sign=azU7Bc002xAAJm6NH7se3vQq4QtcVm6GYCprMPkklU52Rn6SvrLdTxmQrq/X8QNrzL/dNHpT/u3t3HryPcciMu/i6BZulm6GbpZuhm}

生成的参数分别为:x-sgextx-umtx-mini-wuax-sign

Unidbg模拟执行

如果仅是简单直接的模拟执行doCommandNative方法,则回抛出SecExpection 701029904异常,通过网上查阅,发现该方法存在初始化函数。

确定初始化函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
function hook_dlopen(targetSoName) {
const funcName = "android_dlopen_ext"; // old version is "dlopen",
const libc = Module.findBaseAddress("libc.so");
var funcPtr = Module.findExportByName(null, funcName); // 查找导出函数 (null 代表搜索所有模块)

if (funcPtr) {
console.log(`[*] Hooking ${funcName} at libc.so!`);
// hook dlopen
Interceptor.attach(funcPtr, {
onEnter: function (args) {
// args[0] 是要加载的 SO 路径/文件名
this.path = args[0].readCString();
console.log(`[*] 目标App尝试加载So文件: ${this.path}`);
this.shouldHook = false;
if (this.path && this.path.indexOf(targetSoName) >= 0) {
this.shouldHook = true;
}
},
onLeave: function (retval) {
if (this.shouldHook) {
console.log(`[*] ${targetSoName} 加载完成,开始进行Hook操作...`);
// 在这里执行你想要的hook操作
HookTargetMethod();
}
}
});
} else {
console.log("[-] Warning: " + funcName + " not found in exports.");
}
}


// 寻找doCommandNative前置初始化工作(可能是由其他so完成的)
function HookTargetMethod(){
Java.perform(function () {
var targetClass = "com.taobao.wireless.security.adapter.JNICLibrary";

Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if (loader.findClass(targetClass)) {

// 局部化类工厂,避免污染全局
Java.classFactory.loader = loader;
var JNICLibrary = Java.classFactory.use(targetClass);

JNICLibrary["doCommandNative"].implementation = function (i, objArr) {
var result = this.doCommandNative(i, objArr);
//objectArr 可能是一个复杂对象,直接打印可能导致闪退
var stackTrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
var objArrStr = Java.use("java.util.Arrays").toString(objArr);
console.log(`>>> doCommandNative is called: cmdId=${i}, objArr=${objArrStr}\n<<< doCommandNative result=${result}\n${stackTrace}`);
return result;
};
console.log("[*] Hook doCommandNative函数完毕");
}
} catch (e) {}
},
onComplete: function () {}
});
});
}

function main() {
// Hook the target SO file
// 不能通过libsgmain.so判定,因为它本质上是个jar包,不通过dlopen加载
hook_dlopen("libsgmainso-6.6.231201.33656539.so");
}


setImmediate(main);

部分打印如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
...
[*] 目标App尝试加载So文件: /data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libc++_shared.so
[*] 目标App尝试加载So文件: /data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libAliHALogEngine.so
[*] 目标App尝试加载So文件: /data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libsgmainso-6.6.231201.33656539.so
[*] libsgmainso-6.6.231201.33656539.so 加载完成,开始进行Hook操作...
[*] Hook doCommandNative函数完毕
>>> doCommandNative is called: cmdId=10101, objArr=[com.taobao.idlefish.TaoBaoApplication@9bd6cea, 3, , /data/user/0/com.taobao.idlefish/app_SGLib, , com.taobao.idlefish, 7.24.50, 10, com.taobao.idlefish, 0]
<<< doCommandNative result=0
java.lang.Exception
at com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method)
at com.alibaba.wireless.security.mainplugin.б.doCommand(Unknown Source:0)
at com.alibaba.wireless.security.mainplugin.SecurityGuardMainPlugin.onPluginLoaded(Unknown Source:264)
at com.alibaba.wireless.security.framework.d.a(:11)
at com.alibaba.wireless.security.framework.d.c(:1)
at com.alibaba.wireless.security.framework.d.getPluginInfo(:30)
at com.alibaba.wireless.security.open.initialize.b.b(:1)
at com.alibaba.wireless.security.open.initialize.a.loadLibrarySync(:2)
at com.alibaba.wireless.security.open.initialize.a.loadLibrarySync(:1)
at com.alibaba.wireless.security.open.initialize.a.initialize(:1)
at com.taobao.wireless.security.sdk.initialize.a.initialize(:5)
at com.taobao.idlefish.init.SecurityGuardInitConfig.initSecurity(SecurityGuardInitConfig.java:9)
at com.idlefish.blink.BlinkNodeNewJoint.com_taobao_idlefish_init_SecurityGuardInitConfig_initSecurity_main(BlinkNodeNewJoint.java:1)
at com.idlefish.blink.BlinkNodeNewJoint.execNodeByName(BlinkNodeNewJoint.java:1)
at com.taobao.idlefish.launcher.startup.blink.NodeExecutor.execNewNodeByName(NodeExecutor.java:1)
at com.taobao.idlefish.launcher.startup.blinknew.BlinkNewEngine.execNode(BlinkNewEngine.java:39)
at com.taobao.idlefish.launcher.startup.blinknew.BlinkNewEngine.access$300(BlinkNewEngine.java:1)
at com.taobao.idlefish.launcher.startup.blinknew.BlinkNewEngine$ParallelSyncRun.execRun(BlinkNewEngine.java:252)
at com.taobao.idlefish.launcher.startup.blinknew.BlinkNewEngine$ParallelSyncRun.access$000(BlinkNewEngine.java:2)
at com.taobao.idlefish.launcher.startup.blinknew.BlinkNewEngine.execParallelSync(BlinkNewEngine.java:167)
at com.taobao.idlefish.launcher.startup.blinknew.FishNewBlink.now(FishNewBlink.java:17)
at com.taobao.idlefish.launcher.startup.blinknew.FishNewBlink.prepare(FishNewBlink.java:86)
at com.taobao.idlefish.startup.process.MainProcess.onCreate(MainProcess.java:133)
at com.taobao.idlefish.launcher.startup.process.FishProcess.instance(FishProcess.java:20)
at com.taobao.idlefish.TaoBaoApplication.initFishProcess(TaoBaoApplication.java:54)
at com.taobao.idlefish.TaoBaoApplication.newLaunch(TaoBaoApplication.java:34)
at com.taobao.idlefish.TaoBaoApplication.onCreate(TaoBaoApplication.java:44)

>>> doCommandNative is called: cmdId=10102, objArr=[main, 6.6.231201.33656539, /data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libsgmainso-6.6.231201.33656539.so]
<<< doCommandNative result=0
java.lang.Exception
at com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method)
at com.alibaba.wireless.security.mainplugin.б.doCommand(Unknown Source:0)
at com.alibaba.wireless.security.framework.d.a(:11)
at com.alibaba.wireless.security.framework.d.c(:1)
at com.alibaba.wireless.security.framework.d.getPluginInfo(:30)
at com.alibaba.wireless.security.open.initialize.b.b(:1)
at com.alibaba.wireless.security.open.initialize.a.loadLibrarySync(:2)
at com.alibaba.wireless.security.open.initialize.a.loadLibrarySync(:1)
at com.alibaba.wireless.security.open.initialize.a.initialize(:1)
at com.taobao.wireless.security.sdk.initialize.a.initialize(:5)
at com.taobao.idlefish.init.SecurityGuardInitConfig.initSecurity(SecurityGuardInitConfig.java:9)
at com.idlefish.blink.BlinkNodeNewJoint.com_taobao_idlefish_init_SecurityGuardInitConfig_initSecurity_main(BlinkNodeNewJoint.java:1)
at com.idlefish.blink.BlinkNodeNewJoint.execNodeByName(BlinkNodeNewJoint.java:1)
at com.taobao.idlefish.launcher.startup.blink.NodeExecutor.execNewNodeByName(NodeExecutor.java:1)
at com.taobao.idlefish.launcher.startup.blinknew.BlinkNewEngine.execNode(BlinkNewEngine.java:39)
at com.taobao.idlefish.launcher.startup.blinknew.BlinkNewEngine.access$300(BlinkNewEngine.java:1)
at com.taobao.idlefish.launcher.startup.blinknew.BlinkNewEngine$ParallelSyncRun.execRun(BlinkNewEngine.java:252)
at com.taobao.idlefish.launcher.startup.blinknew.BlinkNewEngine$ParallelSyncRun.access$000(BlinkNewEngine.java:2)
at com.taobao.idlefish.launcher.startup.blinknew.BlinkNewEngine.execParallelSync(BlinkNewEngine.java:167)
at com.taobao.idlefish.launcher.startup.blinknew.FishNewBlink.now(FishNewBlink.java:17)
at com.taobao.idlefish.launcher.startup.blinknew.FishNewBlink.prepare(FishNewBlink.java:86)
at com.taobao.idlefish.startup.process.MainProcess.onCreate(MainProcess.java:133)
at com.taobao.idlefish.launcher.startup.process.FishProcess.instance(FishProcess.java:20)
at com.taobao.idlefish.TaoBaoApplication.initFishProcess(TaoBaoApplication.java:54)
at com.taobao.idlefish.TaoBaoApplication.newLaunch(TaoBaoApplication.java:34)
at com.taobao.idlefish.TaoBaoApplication.onCreate(TaoBaoApplication.java:44)

[*] 目标App尝试加载So文件: /data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libsgsecuritybodyso-6.6.231201.33656539.so
>>> doCommandNative is called: cmdId=10603, objArr=[database_encrypt, ]
<<< doCommandNative result=8327dcfe97ed27ebb0a60c3f72835237
java.lang.Exception
at com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method)
at com.alibaba.wireless.security.mainplugin.б.doCommand(Unknown Source:0)
at а.а.а.а.а.й.а.getExtraData(Unknown Source:17)
at а.б.а.а.а.ж.а.getExtraData(Unknown Source:4)
at com.taobao.idlefish.storage.datacenter.DataCenterModule.getEncryptedPassword(DataCenterModule.java:30)
at com.taobao.idlefish.storage.datacenter.DataCenterModule$1.run(DataCenterModule.java:27)
...

>>> doCommandNative is called: cmdId=10401, objArr=[{INPUT=2&reg025RcAN4v7raOUlN1xjvN0BDXA0TY&21407387&1}, 21407387, 3, , true]
<<< doCommandNative result=8f560ed89a67e05aa77eeca4c69e588ddd99e939
java.lang.Exception
at com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method)
at com.alibaba.wireless.security.mainplugin.б.doCommand(Unknown Source:0)
at а.а.а.а.а.з.а.signRequest(Unknown Source:115)
at com.taobao.accs.utl.UtilityImpl.getAppsign(UtilityImpl.java:205)
at com.taobao.accs.data.Message.buildBindApp(Message.java:24)
at com.taobao.accs.data.Message.buildBindApp(Message.java:6)
at com.taobao.accs.data.MessageV2.buildBindApp(MessageV2.java:1)
at com.taobao.accs.internal.ACCSManagerImpl.bindApp(ACCSManagerImpl.java:27)
at com.taobao.agoo.TaobaoRegister.register(TaobaoRegister.java:22)
at com.taobao.fleamarket.PushConfigerNew$6.run(PushConfigerNew.java:40)
at com.taobao.idlefish.launcher.startup.async.AsyncMaybeHandler$2.run(AsyncMaybeHandler.java:3)
...

>>> doCommandNative is called: cmdId=10102, objArr=[securitybody, 6.6.231201.33656539, /data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libsgsecuritybodyso-6.6.231201.33656539.so]
<<< doCommandNative result=0
java.lang.Exception
at com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method)
at com.alibaba.wireless.security.mainplugin.б.doCommand(Unknown Source:0)
at com.alibaba.wireless.security.framework.d.a(:11)
at com.alibaba.wireless.security.framework.d.c(:1)
at com.alibaba.wireless.security.framework.d.getPluginInfo(:30)
at com.alibaba.wireless.security.framework.d.a(Unknown Source:62)
at com.alibaba.wireless.security.framework.d.b(:17)
at com.alibaba.wireless.security.framework.d.getInterface(:10)
at com.alibaba.wireless.security.open.SecurityGuardManager.getInterface(:3)
at com.alibaba.wireless.security.open.SecurityGuardManager.a(:13)
at com.alibaba.wireless.security.open.SecurityGuardManager.getUMIDComp(:3)
at com.ali.user.mobile.info.AppInfo.generateUmidToken(AppInfo.java:56)
at com.ali.user.mobile.info.AppInfo.init(AppInfo.java:166)
at com.ali.user.mobile.app.init.AliUserInit.init(AliUserInit.java:56)
at com.taobao.login4android.login.LoginController.initAliuserSDK(LoginController.java:51)
at com.taobao.login4android.Login$1.run(Login.java:17)
...

>>> doCommandNative is called: cmdId=22302, objArr=[0, true]
<<< doCommandNative result=dfMBm6xLPFZHRwKbpi+ykNIL61XgNNHE
java.lang.Exception
at com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method)
at com.alibaba.wireless.security.mainplugin.б.doCommand(Unknown Source:0)
at com.taobao.dp.DeviceSecuritySDKImpl.getSecurityTokenNative(Unknown Source:25)
at com.taobao.dp.DeviceSecuritySDKImpl.getSecurityToken(Unknown Source:0)
at com.alibaba.wireless.security.open.umid.UMIDComponent.getSecurityToken(Unknown Source:13)
at com.taobao.accs.data.MessageV2.getUmidToken(MessageV2.java:18)
at com.taobao.accs.data.MessageV2.buildBindApp(MessageV2.java:54)
at com.taobao.accs.internal.ACCSManagerImpl.bindApp(ACCSManagerImpl.java:27)
at com.taobao.agoo.TaobaoRegister.register(TaobaoRegister.java:22)
at com.taobao.fleamarket.PushConfigerNew$6.run(PushConfigerNew.java:40)
at com.taobao.idlefish.launcher.startup.async.AsyncMaybeHandler$2.run(AsyncMaybeHandler.java:3)
...

[*] 目标App尝试加载So文件: /data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libsgmiddletierso-6.6.231201.33656539.so
>>> doCommandNative is called: cmdId=10102, objArr=[middletier, 6.6.231201.33656539, /data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libsgmiddletierso-6.6.231201.33656539.so]
<<< doCommandNative result=0
java.lang.Exception
at com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method)
at com.alibaba.wireless.security.mainplugin.б.doCommand(Unknown Source:0)
at com.alibaba.wireless.security.framework.d.a(:11)
at com.alibaba.wireless.security.framework.d.c(:1)
at com.alibaba.wireless.security.framework.d.getPluginInfo(:30)
at com.alibaba.wireless.security.framework.d.a(Unknown Source:62)
at com.alibaba.wireless.security.framework.d.b(:17)
at com.alibaba.wireless.security.framework.d.getInterface(:10)
at com.alibaba.wireless.security.open.SecurityGuardManager.getInterface(:3)
at mtopsdk.security.InnerSignImpl.initMiddleTier(InnerSignImpl.java:46)
at mtopsdk.security.InnerSignImpl.init(InnerSignImpl.java:79)
at mtopsdk.mtop.global.init.InnerMtopInitTask.executeSignTask(InnerMtopInitTask.java:10)
at mtopsdk.mtop.intf.Mtop$1.run(Mtop.java:28)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
at mtopsdk.mtop.util.MtopSDKThreadPoolExecutorFactory$MtopSDKThreadFactory$1.run(MtopSDKThreadPoolExecutorFactory.java:22)

>>> doCommandNative is called: cmdId=10401, objArr=[{INPUT=2&reg025RcAN4v7raOUlN1xjvN0BDXA0TY&21407387&1}, 21407387, 3, , true]
<<< doCommandNative result=8f560ed89a67e05aa77eeca4c69e588ddd99e939
java.lang.Exception
at com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method)
at com.alibaba.wireless.security.mainplugin.б.doCommand(Unknown Source:0)
at а.а.а.а.а.з.а.signRequest(Unknown Source:115)
at com.taobao.accs.utl.UtilityImpl.getAppsign(UtilityImpl.java:205)
at com.taobao.accs.data.Message.buildBindApp(Message.java:24)
at com.taobao.accs.data.Message.buildBindApp(Message.java:6)
at com.taobao.accs.data.MessageV2.buildBindApp(MessageV2.java:1)
at com.taobao.accs.internal.ACCSManagerImpl.bindApp(ACCSManagerImpl.java:27)
at com.taobao.agoo.TaobaoRegister.register(TaobaoRegister.java:22)
at com.taobao.fleamarket.PushConfigerNew$6.run(PushConfigerNew.java:40)
at com.taobao.idlefish.launcher.startup.async.AsyncMaybeHandler$2.run(AsyncMaybeHandler.java:3)
...

>>> doCommandNative is called: cmdId=70201, objArr=[mwua, sgcipher]
<<< doCommandNative result=538233707333
java.lang.Exception
at com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method)
at com.alibaba.wireless.security.mainplugin.б.doCommand(Unknown Source:0)
at com.alibaba.wireless.security.middletierplugin.d.a.a$a.a(Unknown Source:18)
at com.alibaba.wireless.security.middletierplugin.d.a.a.createAVMPInstance(Unknown Source:7)
at mtopsdk.security.InnerSignImpl.getAVMPInstance(InnerSignImpl.java:28)
at mtopsdk.security.InnerSignImpl$1.run(InnerSignImpl.java:5)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
at mtopsdk.mtop.util.MtopSDKThreadPoolExecutorFactory$MtopSDKThreadFactory$1.run(MtopSDKThreadPoolExecutorFactory.java:22)

>>> doCommandNative is called: cmdId=70102, objArr=[21407387, aVvA4IboYJQDAPf8FcEC0UhS&&&21407387&99914b932bd37a50b983c5e7c90ae93b&1768188810&mtop.common.gettimestamp&*&&231200@fleamarket_android_7.24.50&&&&openappkey=DEFAULT_AUTH&27&&&&&&&, false, 0, mtop.common.gettimestamp, pageId=&pageName=, null, null, null, r_1, 0, 0]
<<< doCommandNative result={x-sgext=JBJnkbWnO7OzP0sozzFxPZFWoVaoVbJVqVWnX7JWskSgUKdfoF+pX6BXslehV6FXoVehV6FXoVehV6FEpUShRKFXslehV6FEoUSgRKBEoESgRKBEoEShRKFEoUShRKFEoUShRKFEoUShRLJVsleyV7JXoVehV6FEoVehV6FEoUSjA/BEoUSyVLJQsleyVLId0DTpRKFEsUexV7FHsVeyU85Vt1GoQaNBp16nU6cFqQa3V7dXt1e3V7dXt1e3V7dXtzikOKFBoUGnXqdTpwWpBrdXt1e3V7dXt1e3V84=, x-umt=dfMBm6xLPFZHRwKbpi+ykNIL61XgNNHE, x-mini-wua=a5QSJaFb6JyAMD8KRrjBIJ6IMflm0VTDhzMmsR7IcenIBDQ5d1ZAyfEqKTDv7Y7Hah32lCKXwue9Ls5xrMn1p6vlVkk8lSimJ3VCMrYJOvU2CBks/Iz4k1WKxOzXWebgRVdLtFQXFEVb9WKv+mf6Y/pj+7OXCcGbuTQiY6bLS3a9CR6nIutIbEEab9NptKlZV58Q=, x-sign=azU7Bc002xAALL5pey3MnakNw8fufL5ssMC72inORaSpM451BKgNqdoYzkUEDkVFs0wA2eBv83D9NqoY7S3y2D8IOPy+fL5svny+bL}
java.lang.Exception
at com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative(Native Method)
at com.alibaba.wireless.security.mainplugin.б.doCommand(Unknown Source:0)
at com.alibaba.wireless.security.middletierplugin.d.d.a.a(Unknown Source:370)
at com.alibaba.wireless.security.middletierplugin.d.d.a$a.invoke(Unknown Source:56)
at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
at $Proxy31.getSecurityFactors(Unknown Source)
at mtopsdk.security.InnerSignImpl.getUnifiedSign(InnerSignImpl.java:299)
at mtopsdk.mtop.protocol.builder.impl.InnerProtocolParamBuilderImpl.buildParams(InnerProtocolParamBuilderImpl.java:853)
at mtopsdk.framework.filter.before.ProtocolParamBuilderBeforeFilter.doBefore(ProtocolParamBuilderBeforeFilter.java:6)
at mtopsdk.framework.manager.impl.AbstractFilterManager.start(AbstractFilterManager.java:62)
at mtopsdk.mtop.intf.MtopBuilder$1.run(MtopBuilder.java:56)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
at mtopsdk.mtop.util.MtopSDKThreadPoolExecutorFactory$MtopSDKThreadFactory$1.run(MtopSDKThreadPoolExecutorFactory.java:22)

提取关键方法如下:

1
2
3
4
>>> doCommandNative is called: cmdId=10101, objArr=[com.taobao.idlefish.TaoBaoApplication@9bd6cea, 3, , /data/user/0/com.taobao.idlefish/app_SGLib, , com.taobao.idlefish, 7.24.50, 10, com.taobao.idlefish, 0]
>>> doCommandNative is called: cmdId=10102, objArr=[main, 6.6.231201.33656539, /data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libsgmainso-6.6.231201.33656539.so]
>>> doCommandNative is called: cmdId=10102, objArr=[securitybody, 6.6.231201.33656539, /data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libsgsecuritybodyso-6.6.231201.33656539.so]
>>> doCommandNative is called: cmdId=10102, objArr=[middletier, 6.6.231201.33656539, /data/app/com.taobao.idlefish-ZzE5UcLUySRXY_Pap1bAyQ==/lib/arm64/libsgmiddletierso-6.6.231201.33656539.so]

按顺序依次调用doCommandNative,最后调用doCommandNative生成x-系列参数。

Unidbg补环境

初始代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package com.taobao.idlefish;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.debugger.Debugger;
import com.github.unidbg.debugger.FunctionCallListener;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.file.linux.AndroidFileIO;
import com.github.unidbg.linux.ARM64SyscallHandler;
import com.github.unidbg.linux.AndroidSyscallHandler;
import com.github.unidbg.linux.android.AndroidARM64Emulator;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.api.ApplicationInfo;
import com.github.unidbg.linux.android.dvm.api.ClassLoader;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.android.dvm.wrapper.DvmBoolean;
import com.github.unidbg.linux.android.dvm.wrapper.DvmInteger;
import com.github.unidbg.linux.file.DirectoryFileIO;
import com.github.unidbg.linux.file.SimpleFileIO;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.memory.SvcMemory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.github.unidbg.unix.UnixSyscallHandler;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import com.shopee.Shpssdk;

import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;

public class JNICLibrary extends MyAbstractJni{

private final AndroidEmulator emulator;
private final Memory memory;
private final VM vm;
private final Module module;
private final DvmClass JNICLibraryClzz;

public JNICLibrary(){
emulator = AndroidEmulatorBuilder
.for64Bit()
.addBackendFactory(new Unicorn2Factory(true))
.setProcessName("com.taobao.idlefish")
.setRootDir(new File("unidbg-android/src/test/resources/xianyu/rootfs"))
.build();

emulator.getSyscallHandler().setVerbose(true);

memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));

//加载apk
vm = emulator.createDalvikVM(new File("unidbg-android/src/test/resources/xianyu/xianyu.apk"));
vm.setJni(this);
vm.setVerbose(true);

// 使用 libandroid.so 虚拟模块, 需早于目标 SO 加载的时机
new AndroidModule(emulator, vm).register(memory);
// 加载目标库
DalvikModule dm = vm.loadLibrary("sgmainso-6.6.231201.33656539", true);
module = dm.getModule();

JNICLibraryClzz = vm.resolveClass("com.taobao.wireless.security.adapter.JNICLibrary");
// 手动执行JNI_OnLoad函数
dm.callJNI_OnLoad(emulator);

}

private void calldoCommandNative() {
// 10101 app_SGLib
int cmdId1 = 10101;
ArrayObject obj1 = new ArrayObject(
vm.resolveClass("com.taobao.idlefish.TaoBaoApplication").newObject(null),
DvmInteger.valueOf(vm,3),
new StringObject(vm, ""),
new StringObject(vm, "/data/user/0/com.taobao.idlefish/app_SGLib"),
new StringObject(vm, ""),
new StringObject(vm, "com.taobao.idlefish"),
new StringObject(vm, "7.24.50"),
new StringObject(vm, "10"),
new StringObject(vm, "com.taobao.idlefish"),
DvmInteger.valueOf(vm, 0)
);
System.out.println("[+] ---------------------------------- 10101 app_SGLib --------------------------------");
String result1 = JNICLibraryClzz.callStaticJniMethodObject(emulator,
"doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;",
cmdId1, obj1)
.getValue().toString();
System.out.println("[+] app_SGLib init, result: " + result1);

// 10102 libsgmainso-6.6.231201.33656539.so
int cmdId2 = 10102;
ArrayObject obj2 = new ArrayObject(
new StringObject(vm, "main"),
new StringObject(vm, "6.6.231201.33656539"),
new StringObject(vm, "/data/app/com.taobao.idlefish-Mk5qkEh32zCm7x9fbp9IBw==/lib/arm64/libsgmainso-6.6.231201.33656539.so")
);
System.out.println("[+] ---------------------------------- 10102 libsgmainso-6.6.231201.33656539.so --------------------------------");
String result2 = JNICLibraryClzz.callStaticJniMethodObject(emulator,
"doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;",
cmdId2, obj2)
.getValue().toString();
System.out.println("[+] libsgmainso-6.6.231201.33656539.so init, result: " + result2);

// 10102 libsgsecuritybodyso-6.6.231201.33656539.so
int cmdId3 = 10102;
ArrayObject obj3 = new ArrayObject(
new StringObject(vm, "securitybody"),
new StringObject(vm, "6.6.231201.33656539"),
new StringObject(vm, "/data/app/com.taobao.idlefish-Mk5qkEh32zCm7x9fbp9IBw==/lib/arm64/libsgsecuritybodyso-6.6.231201.33656539.so")
);
System.out.println("[+] ---------------------------------- 10102 libsgsecuritybodyso-6.6.231201.33656539.so --------------------------------");
String result3 = JNICLibraryClzz.callStaticJniMethodObject(emulator,
"doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;",
cmdId3, obj3)
.getValue().toString();
System.out.println("[+] libsgsecuritybodyso-6.6.231201.33656539.so init, result: " + result3);

// 10102 libsgmiddletierso-6.6.231201.33656539.so
int cmdId4 = 10102;
ArrayObject obj4 = new ArrayObject(
new StringObject(vm, "middletier"),
new StringObject(vm, "6.6.231201.33656539"),
new StringObject(vm, "/data/app/com.taobao.idlefish-Mk5qkEh32zCm7x9fbp9IBw==/lib/arm64/libsgmiddletierso-6.6.231201.33656539.so")
);
System.out.println("[+] ---------------------------------- 10102 libsgmiddletierso-6.6.231201.33656539.so --------------------------------");
String result4 = JNICLibraryClzz.callStaticJniMethodObject(emulator,
"doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;",
cmdId4, obj4)
.getValue().toString();
System.out.println("[+] libsgmiddletierso-6.6.231201.33656539.so init, result: " + result4);

// 70102 x-系列参数生成
int cmdId5 = 70102;
ArrayObject obj5 = new ArrayObject(new StringObject(vm, "21407387"),
new StringObject(vm, "aVvA4IboYJQDAPf8FcEC0UhS&&&21407387&a0fe5c4111c15cc20e9c7d1e435d1e4a&1767857182&mtop.taobao.idlemtopsearch.search&1.0&&231200@fleamarket_android_7.24.50&&0&0&openappkey=DEFAULT_AUTH&27&&&&&&&"),
DvmBoolean.valueOf(vm, false),
DvmInteger.valueOf(vm, 0),
new StringObject(vm, "mtop.taobao.idlemtopsearch.search"),
new StringObject(vm, "pageId=&pageName="),
null, null, null,
new StringObject(vm, "r_43"),
DvmInteger.valueOf(vm, 0),
DvmInteger.valueOf(vm, 0)
);
System.out.println("[+] ---------------------------------- 70102 x-系列参数生成 --------------------------------");
String result5 = JNICLibraryClzz.callStaticJniMethodObject(emulator,
"doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;",
cmdId5, obj5)
.getValue()
.toString();
System.out.println("call doCommandNative function: " + result5);
}

public static void main(String[] args) {
JNICLibrary jnicLibrary = new JNICLibrary();
jnicLibrary.calldoCommandNative();
}
}

报错

1
2
3
4
JNIEnv->GetStaticMethodID(com/alibaba/wireless/security/mainplugin/SecurityGuardMainPlugin.getMainPluginClassLoader()Ljava/lang/ClassLoader;) => 0xcc338928 was called from RX@0x1207181c[libmain.so]0x7181c
[16:51:22 404] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-869037784, svcNumber=0x16e, PC=unidbg@0xfffe0774, LR=RX@0x1207183c[libmain.so]0x7183c, syscall=null
java.lang.UnsupportedOperationException: com/alibaba/wireless/security/mainplugin/SecurityGuardMainPlugin->getMainPluginClassLoader()Ljava/lang/ClassLoader;
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:433)
1
2
3
4
case "com/alibaba/wireless/security/mainplugin/SecurityGuardMainPlugin->getMainPluginClassLoader()Ljava/lang/ClassLoader;": {
// return vm.resolveClass("java/lang/ClassLoader").newObject(null);
return new ClassLoader(vm, signature);
}

报错

1
2
3
4
JNIEnv->GetMethodID(com/taobao/idlefish/TaoBaoApplication.getPackageCodePath()Ljava/lang/String;) => 0xfc688c30 was called from RX@0x12028ab0[libmain.so]0x28ab0
[16:52:01 382] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-130448, svcNumber=0x11e, PC=unidbg@0xfffe0274, LR=RX@0x12028ad0[libmain.so]0x28ad0, syscall=null
java.lang.UnsupportedOperationException: com/taobao/idlefish/TaoBaoApplication->getPackageCodePath()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:933)

通过ActivityThread获取当前Application实例,然后调用相应方法获取返回值。

1
2
3
case "com/taobao/idlefish/TaoBaoApplication->getPackageCodePath()Ljava/lang/String;":{
return new StringObject(vm, "/data/app/com.taobao.idlefish-Mk5qkEh32zCm7x9fbp9IBw==/base.apk");
}

报错

1
2
3
4
JNIEnv->GetMethodID(com/taobao/idlefish/TaoBaoApplication.getFilesDir()Ljava/io/File;) => 0x91796021 was called from RX@0x12028b20[libmain.so]0x28b20
[16:54:14 511] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-130448, svcNumber=0x11e, PC=unidbg@0xfffe0274, LR=RX@0x12028b40[libmain.so]0x28b40, syscall=null
java.lang.UnsupportedOperationException: com/taobao/idlefish/TaoBaoApplication->getFilesDir()Ljava/io/File;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:933)

可能需要文件路径重定向?毕竟new File是电脑端执行的,映射的是本地路径。

1
2
3
4
case "com/taobao/idlefish/TaoBaoApplication->getFilesDir()Ljava/io/File;": {
String path = "/data/user/0/com.taobao.idlefish/files";
return vm.resolveClass("java/io/File").newObject(new File(path));
}

接下来就是这个文件的方法

1
2
3
4
JNIEnv->GetMethodID(java/io/File.getAbsolutePath()Ljava/lang/String;) => 0xb4553f34 was called from RX@0x12028b88[libmain.so]0x28b88
[16:58:01 701] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-130448, svcNumber=0x11e, PC=unidbg@0xfffe0274, LR=RX@0x12028ba8[libmain.so]0x28ba8, syscall=null
java.lang.UnsupportedOperationException: java/io/File->getAbsolutePath()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:933)

不能直接获取File对象并执行相应方法,刚才说过,路径是本地的

1
2
3
4
5
6
7
8
case "java/io/File->getAbsolutePath()Ljava/lang/String;": {
File file = (File)dvmObject.getValue();
// 会带上盘符!!!
String absolutePath = file.getAbsolutePath();
System.out.println("java/io/File->getAbsolutePath() called: " + absolutePath);
// return new StringObject(vm, absolutePath);
return new StringObject(vm, "/data/user/0/com.taobao.idlefish/files");
}

报错

1
2
3
4
5
JNIEnv->GetMethodID(com/taobao/idlefish/TaoBaoApplication.getApplicationInfo()Landroid/content/pm/ApplicationInfo;) => 0x73a4c4f9 was called from RX@0x12028bf0[libmain.so]0x28bf0
[17:00:15 872] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-130448, svcNumber=0x11e, PC=unidbg@0xfffe0274, LR=RX@0x12028c3c[libmain.so]0x28c3c, syscall=null
java.lang.UnsupportedOperationException: com/taobao/idlefish/TaoBaoApplication->getApplicationInfo()Landroid/content/pm/ApplicationInfo;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:933)
at com.taobao.idlefish.MyAbstractJni.callObjectMethod(MyAbstractJni.java:48)
1
2
3
case "com/taobao/idlefish/TaoBaoApplication->getApplicationInfo()Landroid/content/pm/ApplicationInfo;": {
return new ApplicationInfo(vm);
}

报错

1
2
3
4
JNIEnv->GetFieldID(android/content/pm/ApplicationInfo.nativeLibraryDir Ljava/lang/String;) => 0x1fabc7e8 was called from RX@0x1202931c[libmain.so]0x2931c
[17:01:34 211] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-129472, svcNumber=0x15b, PC=unidbg@0xfffe0644, LR=RX@0x12029360[libmain.so]0x29360, syscall=null
java.lang.UnsupportedOperationException: android/content/pm/ApplicationInfo->nativeLibraryDir:Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.getObjectField(AbstractJni.java:171)
1
2
3
case "android/content/pm/ApplicationInfo->nativeLibraryDir:Ljava/lang/String;": {
return new StringObject(vm, "/data/app/com.taobao.idlefish-Mk5qkEh32zCm7x9fbp9IBw==/lib/arm64");
}

报错

1
2
3
4
JNIEnv->NewStringUTF("sgCallBackUniqueKey") was called from RX@0x12109164[libmain.so]0x109164
[17:03:46 856] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-125376, svcNumber=0x17d, PC=unidbg@0xfffe0864, LR=RX@0x1205bd94[libmain.so]0x5bd94, syscall=null
java.lang.UnsupportedOperationException: com/uc2/crashsdk/JNIBridge->registerInfoCallback(Ljava/lang/String;IJI)I
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticIntMethod(AbstractJni.java:201

遍历的classloader并没有找到该类,取值参考了https://bbs.kanxue.com/thread-268927-1.htm

1
2
3
4
5
6
7
8
case "com/uc2/crashsdk/JNIBridge->registerInfoCallback(Ljava/lang/String;IJI)I": {
String arg1 = varArg.getObjectArg(0).getValue().toString();
int arg2 = varArg.getIntArg(1);
long arg3 = varArg.getLongArg(2);
int arg4 = varArg.getIntArg(3);
System.out.println("registerInfoCallback called: arg1 = " + arg1 +", arg2 = " + arg2 + ", arg3 = " + arg3 + ", arg4 = " + arg4);
return 1;// 1 0: 多一个maps的读取
}

报错

1
2
3
4
JNIEnv->CallStaticIntMethod(class com/uc2/crashsdk/JNIBridge, registerInfoCallback("sgCallBackUniqueKey", 0x101, 0x1205b0ccL, 0x1) => 0x1) was called from RX@0x1205bd94[libmain.so]0x5bd94
[17:06:45 689] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-129120, svcNumber=0x171, PC=unidbg@0xfffe07a4, LR=RX@0x1202a9a0[libmain.so]0x2a9a0, syscall=null
java.lang.UnsupportedOperationException: com/alibaba/wireless/security/framework/ApmMonitorAdapter->isEnableFullTrackRecord()Z
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticBooleanMethod(AbstractJni.java:181)

静态方法,那就使用frida主动调用获取具体值

1
2
3
case "com/alibaba/wireless/security/framework/ApmMonitorAdapter->isEnableFullTrackRecord()Z": {
return false;
}

报错

1
2
3
4
[17:09:44 393]  INFO [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:712) - stat64 pathname=/data/user/0/com.taobao.idlefish/app_SGLib/.sf/.nee
[17:09:44 397] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-130544, svcNumber=0x118, PC=unidbg@0xfffe0214, LR=RX@0x1210a3d0[libmain.so]0x10a3d0, syscall=null
java.lang.UnsupportedOperationException: java/lang/Integer-><init>(I)V
at com.github.unidbg.linux.android.dvm.AbstractJni.newObject(AbstractJni.java:763)
1
2
3
4
5
case "java/lang/Integer-><init>(I)V":{
int arg1 = varArg.getIntArg(0);
System.out.println("java/lang/Integer-><init>(I)V called: " + arg1);
return vm.resolveClass("java/lang/Integer").newObject(arg1);
}

重新运行后,发现跑通了10101 app_SGLib以及10102 libsgmainso-6.6.231201.33656539.so。之后就存在一个致命报错

1
2
3
4
JNIEnv->GetStaticFieldID(com/alibaba/wireless/security/framework/SGPluginExtras.slotJ) => 0x24d12279 was called from RX@0x12029b0c[libmain.so]0x29b0c
[17:11:47 299] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-128576, svcNumber=0x193, PC=unidbg@0xfffe09c4, LR=RX@0x12029b30[libmain.so]0x29b30, syscall=null
java.lang.UnsupportedOperationException: com/alibaba/wireless/security/framework/SGPluginExtras->slot:J
at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticLongField(AbstractJni.java:1084)

slot值没有设置初始值就直接获取,尝试如下修复

1
2
3
4
5
private long slot = 0L;

case "com/alibaba/wireless/security/framework/SGPluginExtras->slot:J": {
return slot;
}

然后就出现了libsgsecuritybodyso dlopen失败的报错(一度怀疑slot跟libsgsecuritybodyso的加载有关,卡了好久)

1
2
3
4
[17:16:42 416]  INFO [com.github.unidbg.linux.android.ArmLD64] (ArmLD64:308) - dlopen failed: /data/app/com.taobao.idlefish-Mk5qkEh32zCm7x9fbp9IBw==/lib/arm64/libsgsecuritybodyso-6.6.231201.33656539.so
[17:16:42 417] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=302161312, svcNumber=0x1f1, PC=unidbg@0xfffe174c[libandroid.so]0x74c, LR=RX@0x12029dac[libmain.so]0x29dac, syscall=null
com.github.unidbg.InvalidMemoryAccessException
at com.github.unidbg.pointer.UnidbgPointer.write(UnidbgPointer.java:168)

经查询,该文件确实存在于rootfs目录下,且文件路径没有问题。发给gpt,告诉我可以手动加载该库。

1
2
DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/xianyu/rootfs/data/app/com.taobao.idlefish-Mk5qkEh32zCm7x9fbp9IBw==/lib/arm64/libsgsecuritybodyso-6.6.231201.33656539.so"), true);
dm.callJNI_OnLoad(emulator);

没想到居然成功了!接下来报错

1
2
3
4
JNIEnv->GetStaticMethodID(com/alibaba/wireless/security/securitybody/SecurityGuardSecurityBodyPlugin.getPluginClassLoader()Ljava/lang/ClassLoader;) => 0xbc565e30 was called from RX@0x1311d7d8[libsecuritybody.so]0x1d7d8
[17:42:39 706] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-1135190480, svcNumber=0x16e, PC=unidbg@0xfffe0774, LR=RX@0x1311d7f8[libsecuritybody.so]0x1d7f8, syscall=null
java.lang.UnsupportedOperationException: com/alibaba/wireless/security/securitybody/SecurityGuardSecurityBodyPlugin->getPluginClassLoader()Ljava/lang/ClassLoader;
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:433)

这次参考刚才的给出的文章链接

1
2
3
case "com/alibaba/wireless/security/securitybody/SecurityGuardSecurityBodyPlugin->getPluginClassLoader()Ljava/lang/ClassLoader;": {
return vm.resolveClass("dalvik/system/PathClassLoader").newObject(this.getClass().getClassLoader());
}

报错

1
2
3
4
JNIEnv->GetStaticMethodID(com/taobao/dp/util/CallbackHelper.getInstance()Lcom/taobao/dp/util/CallbackHelper;) => 0x70e6476c was called from RX@0x1311dc14[libsecuritybody.so]0x1dc14
[17:48:30 721] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=1894139756, svcNumber=0x16e, PC=unidbg@0xfffe0774, LR=RX@0x1311dc34[libsecuritybody.so]0x1dc34, syscall=null
java.lang.UnsupportedOperationException: com/taobao/dp/util/CallbackHelper->getInstance()Lcom/taobao/dp/util/CallbackHelper;
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:433)

返回对应类的空对象进行占位

1
2
3
case "com/taobao/dp/util/CallbackHelper->getInstance()Lcom/taobao/dp/util/CallbackHelper;": {
return vm.resolveClass("com/taobao/dp/util/CallbackHelper").newObject(null);
}

报错

1
2
3
4
JNIEnv->GetStaticMethodID(com/alibaba/wireless/security/securitybody/InvocationHandlerAdapter.getClassLoader()Ljava/lang/ClassLoader;) => 0x34a7c06 was called from RX@0x13107f68[libsecuritybody.so]0x7f68
[17:50:35 328] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-129168, svcNumber=0x16e, PC=unidbg@0xfffe0774, LR=RX@0x13107f94[libsecuritybody.so]0x7f94, syscall=null
java.lang.UnsupportedOperationException: com/alibaba/wireless/security/securitybody/InvocationHandlerAdapter->getClassLoader()Ljava/lang/ClassLoader;
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:433)
1
2
3
case "com/alibaba/wireless/security/securitybody/InvocationHandlerAdapter->getClassLoader()Ljava/lang/ClassLoader;": {
return new ClassLoader(vm, signature);
}

接下来终于到了slot的SetStaticLongField报错

1
2
3
4
JNIEnv->SetStaticLongField(class com/alibaba/wireless/security/framework/SGPluginExtras, slot, 0x1310528b) was called from RX@0x13138e5c[libsecuritybody.so]0x38e5c
[17:52:29 510] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=617685625, svcNumber=0x19c, PC=unidbg@0xfffe0a54, LR=RX@0x13138e5c[libsecuritybody.so]0x38e5c, syscall=null
java.lang.UnsupportedOperationException: com/alibaba/wireless/security/framework/SGPluginExtras->slot:J
at com.github.unidbg.linux.android.dvm.AbstractJni.setStaticLongField(AbstractJni.java:1044)
1
2
3
4
case "com/alibaba/wireless/security/framework/SGPluginExtras->slot:J": {
slot = value;
return;
}

报错

1
2
3
4
JNIEnv->NewStringUTF("com/alibaba/wireless/security/securitybody/ApmSecurityBodyPluginAdapter") was called from RX@0x1311d444[libsecuritybody.so]0x1d444
[17:54:43 740] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-130448, svcNumber=0x11e, PC=unidbg@0xfffe0274, LR=RX@0x1311d464[libsecuritybody.so]0x1d464, syscall=null
java.lang.UnsupportedOperationException: dalvik/system/PathClassLoader->findClass(Ljava/lang/String;)Ljava/lang/Class;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:933)

获取类名参数,通过vm.resolveClass让模拟器自己去找

1
2
3
4
5
case "dalvik/system/PathClassLoader->findClass(Ljava/lang/String;)Ljava/lang/Class;": {
// 获取类名
String clazz = varArg.getObjectArg(0).getValue().toString();
return vm.resolveClass(clazz);
}

报错,看样子来到了设备信息采集环节了

1
2
3
[18:01:18 986]  WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-129168, svcNumber=0x16e, PC=unidbg@0xfffe0774, LR=RX@0x12046d68[libmain.so]0x46d68, syscall=null
java.lang.UnsupportedOperationException: com/taobao/wireless/security/adapter/datacollection/DeviceInfoCapturer->doCommandForString(I)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:433)

通过枚举类加载器,确认该类在/data/app/com.taobao.idlefish-Mk5qkEh32zCm7x9fbp9IBw==/lib/arm64/libsgmain.so文件中

静态方法的话,在加载libsgmain.so后hook 该函数并主动调用。

1
2
3
case 122:{
return new StringObject(vm,"com.taobao.idlefish");
}

报错

1
2
3
4
File closed '/data/user/0/com.taobao.idlefish/files/0a231bd8575dcf72.txt' from RX@0x1231ece4[libc.so]0x1ece4
[19:57:59 936] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=303624712, svcNumber=0x17d, PC=unidbg@0xfffe0864, LR=RX@0x120546fc[libmain.so]0x546fc, syscall=null
java.lang.UnsupportedOperationException: com/alibaba/wireless/security/framework/utils/UserTrackMethodJniBridge->utAvaiable()I
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticIntMethod(AbstractJni.java:201)

同刚才所述方法以获取返回值

1
2
3
case "com/alibaba/wireless/security/framework/utils/UserTrackMethodJniBridge->utAvaiable()I": {
return 1;
}

重新运行后,终于跑通了10102 libsgsecuritybodyso-6.6.231201.33656539.so10102 libsgmiddletierso-6.6.231201.33656539.so。接下来就是与70102 x-系列参数生成相关的报错了。

非常可惜!!!补了这么久的环境,结果到头来还是抛出 SecException 701029904报错!这说明初始化工作还是未完成!




根据日志实现了其他早于70102的调用号,然而还是没有用。以下是相关调用号的实现,比较杂乱。

继续根据日志查找早于70102的调用号,在他之上是

1
2
3
4
5
>>> doCommandNative is called: cmdId=70201, objArr=[mwua, sgcipher]
[+] Object Array length: 2
[0] (java.lang.String) mwua
[1] (java.lang.String) sgcipher
<<< doCommandNative result=513661310394

unidbg里调用发现此处出现同70102的SecException 702019904。同样也走到了SecException处。根据之前的分析,很可能是marjor_cmd(7)模块还未挂载

1
2
3
4
[+] ---------------------------------- cmdId=70201, objArr=[mwua, sgcipher] --------------------------------
Find native function Java_com_taobao_wireless_security_adapter_JNICLibrary_doCommandNative => RX@0x120280c4[libmain.so]0x280c4
JNIEnv->NewStringUTF("") was called from RX@0x12109c4c[libmain.so]0x109c4c
SecException{message: , errorCode: 702019904}

继续往前追溯,找不是7的

1
2
3
4
5
>>> doCommandNative is called: cmdId=10602, objArr=[0, null]
[+] Object Array length: 2
[0] (java.lang.Integer) 0
[1] null
<<< doCommandNative result=21407387

实现了但是没什么用

然后发现唯一调用号13806

1
2
3
4
>>> doCommandNative is called: cmdId=13806, objArr=[0]
[+] Object Array length: 1
[0] (java.lang.Integer) 0
<<< doCommandNative result=null

也没用

1
2
3
4
[+] ---------------------------------- cmdId=70101, objArr=[null, 0] --------------------------------
Find native function Java_com_taobao_wireless_security_adapter_JNICLibrary_doCommandNative => RX@0x120280c4[libmain.so]0x280c4
JNIEnv->NewStringUTF("") was called from RX@0x12109c4c[libmain.so]0x109c4c
SecException{message: , errorCode: 701019904}

再次证明主模块7未加载

然后是22302调用号

1
2
3
4
5
>>> doCommandNative is called: cmdId=22302, objArr=[0, false]
[+] Object Array length: 2
[0] (java.lang.Integer) 0
[1] (java.lang.Boolean) false
<<< doCommandNative result=8D8BTHxLPMsF2QKbsklmu1VBFpGxb3W4

添加后,提示补环境

1
2
3
4
DeviceInfoCapturer->doCommandForString called: arg1 = 135
[21:59:52 098] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-129168, svcNumber=0x16e, PC=unidbg@0xfffe0774, LR=RX@0x12046d68[libmain.so]0x46d68, syscall=null
java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 1
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
1
2
3
4
5
6
7
8
9
10
11
12
case "com/taobao/wireless/security/adapter/datacollection/DeviceInfoCapturer->doCommandForString(I)Ljava/lang/String;": {
int arg1 = varArg.getIntArg(0);
System.out.println("DeviceInfoCapturer->doCommandForString called: arg1 = " + arg1);
switch (arg1){
case 122:{
return new StringObject(vm, "com.taobao.idlefish");
}
case 135:{
return new StringObject(vm, "aWTitjAtFNcDALD1RXzM/mEk");
}
}
}

然后发现aWTitjAtFNcDALD1RXzM/mEk的值是调用号70102的参数的一部分

继续补环境报错

1
2
3
4
SPUtility2->readFromSPUnified called: arg1=LOCAL_DEVICE_INFO, arg2=982c1b269b8e023e5aede2421cbf9c48, arg3=null
[22:06:59 394] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-129168, svcNumber=0x16e, PC=unidbg@0xfffe0774, LR=RX@0x120f1cbc[libmain.so]0xf1cbc, syscall=null
java.lang.UnsupportedOperationException: com/taobao/wireless/security/adapter/common/SPUtility2->readFromSPUnified(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:433)

通过frida hook获取该值

1
2
>>> readFromSPUnified is called: str1=LOCAL_DEVICE_INFO, str2=982c1b269b8e023e5aede2421cbf9c48, str3=null
<<< readFromSPUnified result=aWTitjAtFNcDALD1RXzM/mEk

最终发现模拟执行的返回值和真机的不一样!!!

1
[+] doCommandNative(cmdId=22302, objArr=[0, false]), result: aWTitjAtFNcDALD1RXzM/mEk

这说明环境没补对。

然后是10401调用号

1
2
3
4
5
6
7
8
>>> doCommandNative is called: cmdId=10401, objArr=[{INPUT=2&reg02fsZKlvv85CN39rSMkUn0BHcUkUV&21407387&1}, 21407387, 3, , true]
[+] Object Array length: 5
[0] (java.util.HashMap) {INPUT=2&reg02fsZKlvv85CN39rSMkUn0BHcUkUV&21407387&1}
[1] (java.lang.String) 21407387
[2] (java.lang.Integer) 3
[3] (java.lang.String)
[4] (java.lang.Boolean) true
<<< doCommandNative result=d61e098f56394a1221d99ece493fc3bdb0367559

报错

1
2
3
4
5
JNIEnv->GetObjectArrayElement([java.util.HashMap@1b1426f4, "21407387", java.lang.Integer@32b260fa, "", true], 0) => java.util.HashMap@1b1426f4 was called from RX@0x1210cf10[libmain.so]0x10cf10
[11:36:01 011] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-130448, svcNumber=0x11e, PC=unidbg@0xfffe0274, LR=RX@0x1210cf90[libmain.so]0x10cf90, syscall=null
java.lang.UnsupportedOperationException: java/util/HashMap->keySet()Ljava/util/Set;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:933)
at com.taobao.idlefish.MyAbstractJni.callObjectMethod(MyAbstractJni.java:116)
1
2
3
4
5
6
case "java/util/HashMap->keySet()Ljava/util/Set;": {
HashMap hashMap = (HashMap) dvmObject.getValue();
System.out.println("java/util/HashMap->keySet() called: " + hashMap);
Set set = hashMap.keySet();
return vm.resolveClass("java/util/Set").newObject(set);
}

报错

1
2
3
[11:47:59 356]  WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=48208774, svcNumber=0x11e, PC=unidbg@0xfffe0274, LR=RX@0x1210cfb0[libmain.so]0x10cfb0, syscall=null
java.lang.UnsupportedOperationException: java/util/Set->toArray()[Ljava/lang/Object;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:933)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
case "java/util/Set->toArray()[Ljava/lang/Object;": {
Set set = (Set)dvmObject.getValue();
Object[] objects = set.toArray();
// 准备存储 DvmObject 的数组
DvmObject<?>[] dvmObjects = new DvmObject[objects.length];
for (int i = 0; i < objects.length; i++) {
// 这里需要根据实际类型转换,如果是 String 则用 StringObject,如果是 Map 用 MapObject 等
// 或者简单地通过 ProxyDvmObject 转换
dvmObjects[i] = ProxyDvmObject.createObject(vm, objects[i]);
}

// 返回一个 ArrayObject,它在 JNI 层对应 [Ljava/lang/Object;
return new ArrayObject(dvmObjects);
}

报错

1
2
3
[19:15:08 572]  WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:410) - handleInterrupt intno=2, NR=-130448, svcNumber=0x11e, PC=unidbg@0xfffe0274, LR=RX@0x1210d030[libmain.so]0x10d030, syscall=null
java.lang.UnsupportedOperationException: java/util/HashMap->get(Ljava/lang/Object;)Ljava/lang/Object;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:933)
1
2
3
4
5
6
case "java/util/HashMap->get(Ljava/lang/Object;)Ljava/lang/Object;": {
HashMap hashMap = (HashMap) dvmObject.getValue();
Object arg1 = varArg.getObjectArg(0).getValue();
Object result = hashMap.get(arg1);
return vm.resolveClass("java/lang/Object").newObject(result);
}

补完之后,出现SecException报错

1
SecException{message: , errorCode: 601}

搜索发现是参数不正确。

后续待补充…..


参考:

unidbg调用sgmain的doCommandNative函数生成某酷encryptR_client参数

关于unidbg调试某app的libsgmainso文件出现的SecException(1910)问题-Android安全-看雪安全社区|专业技术交流与安全研究论坛

unidbg调用sgmain的doCommandNative函数生成某酷encryptR_client参数

[分享]9.23 mtop 6.5.27

安卓逆向小案例——阿里系某电影票务APP加密参数还原-Unidbg篇_阿里系app逆向-CSDN博客


某鱼App逆向
http://example.com/2026/01/14/逆向实战/闲鱼App逆向/
作者
gla2xy
发布于
2026年1月14日
许可协议