Bypassing X‑Product Search Encryption with Frida RPC and Python
This tutorial demonstrates how to reverse‑engineer the X‑Product Android app, locate the dynamic newSign parameter, hook the RequestUtils.c method with Frida, and expose it via an RPC‑based FastAPI service to generate valid signed requests for the search API.
Disclaimer
This tutorial is for learning and discussion only; any illegal use is prohibited.
Preface
Hello, I am "Code Farmer Saturday" and will show how to use Frida RPC to forward calls and obtain the encrypted newSign value required by the X‑Product search API.
Target App
x物 4.74.5 version
Link: https://pan.baidu.com/s/1el0a48vsIl7XI-cDr7iynA
Password: tlvbEnvironment
pixel2 v10 (rooted)
Magisk v23.0
Charles v4.6.2
Drony v1.3.154
Python v3.8.6
frida v14.2.18Packet Capture
By capturing the search request we find a URL whose newSign value changes each time.
The newSign parameter is required; without it the server returns a signature verification error.
Simple Analysis
Decompile the APK with jadx and search for newSign. The method RequestUtils.c is responsible for generating it.
Hook the first occurrence of RequestUtils.c to inspect its arguments.
Java.perform(function () {
var map = Java.use("java.util.HashMap").$new();
// populate map with arguments
var result = Java.use("com.shizhuang.duapp.common.utils.RequestUtils").c(map, j2);
return result;
});Verification
Running the hook confirms that the intercepted newSign matches the one required by the server.
RPC Forwarding
Expose the hooked method via Frida RPC so that a remote Python service can request the signature.
jsCode = """
function newsign(arg_f, j2) {
var map = Java.use("java.util.HashMap").$new();
for (var key in arg_f) {
map.put(key + "", arg_f[key] + "");
}
return Java.use("com.shizhuang.duapp.common.utils.RequestUtils").c(map, j2);
}
rpc.exports = { newsign: newsign };
"""FastAPI RPC Server
from fastapi import FastAPI
import uvicorn, frida
jsCode = """... (as above) ..."""
process = frida.get_usb_device().attach('com.shizhuang.duapp')
script = process.create_script(jsCode)
script.load()
app = FastAPI()
class Item(BaseModel):
m: dict
j2: int
@app.post("/getnewsign")
async def getencrypt(item: Item):
result = script.exports.newsign(item.m, item.j2)
return {"data": result}
if __name__ == '__main__':
uvicorn.run(app, port=8080)Requesting the Search API
import requests, time, json
timestamp = int(time.time()*1000)
params = {"originSearch":"false", "catId":0, "abTest":"[{'name':'search_equlheight_spu_strategy','value':'0'}]", "hideAddProduct":0, "sortType":0, "showHot":1, "limit":20, "productDetailVersionFlag":1, "typeId":0, "sortMode":0, "page":0, "title":"13苹果"}
data = {"m": params, "j2": timestamp}
sign_resp = requests.post('http://127.0.0.1:8080/getnewsign', data=json.dumps(data))
params["newSign"] = sign_resp.json().get("data")
headers = {"duuuid":"38a763c08fd35e88", "duimei":"", "duplatform":"android", "appId":"duapp", "duchannel":"pp", "duv":"4.74.5", "dudeviceTrait":"Pixel+2", "dudeviceBrand":"google", "timestamp":f"{timestamp}", "User-Agent":"duapp/4.74.5(android;10)", "X-Auth-Token":"<token>", "isRoot":"0", "emu":"0", "isProxy":"0", "Host":"app.dewu.com", "Accept-Encoding":"gzip", "Connection":"keep-alive"}
response = requests.get(url="https://app.dewu.com/api/v1/app/search/ice/search/list", headers=headers, params=params)
print(response.text)Result
Conclusion
The RPC approach successfully retrieves the dynamic newSign value, enabling automated requests to the X‑Product search API.
Python Crawling & Data Mining
Life's short, I code in Python. This channel shares Python web crawling, data mining, analysis, processing, visualization, automated testing, DevOps, big data, AI, cloud computing, machine learning tools, resources, news, technical articles, tutorial videos and learning materials. Join us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
