折腾京东APP签名

您所在的位置:网站首页 京东扫码登录有效期 折腾京东APP签名

折腾京东APP签名

2024-06-14 19:13| 来源: 网络整理| 查看: 265

9BJQUycoguMsovYxJ84M.png

前言

前几天随手撸了个自动上传京东open token(open token其实就是用户cookie)的脚本(有兴趣可以看看:京东cookie自动更新维护),虽然可以自动上传了,但是每天都得去打开京东APP才行,而且两次打开时间不能超过24小时,不然open token就过期了。这方法怎么看都不是很省事!

最近一直在找替代方法,发现Zy143L大佬脚本中提到wskey可以申请open token,wskey有效期应该比较长,我使用好几天之前抓包到的wskey依然可以获取到open token。wskey在APP抓包中经常看到,使用wskey的请求里面都有签名,感觉签名方法应该是一样的。通过genToken的接口获取open token,那个签名方法虽然短时间可以重复使用(请求只需要变动wskey就好,签名没有加入wskey做计算),但是还不清楚有效期,如果一直靠抓包获取签名的话那比上面直接抓包open token还恶心!

分析

wskey很重要,京东APP很多敏感接口都是使用wskey,不要泄露!!!

既然可以通过wskey获取open token,wskey有效期有比较长,那么它是一个很好代替每天上传open token的方法。

我的思路:

不定期通过手机app抓包wskey上传到服务器,服务器接收到后写入wskeys.list。 每天定时通过wskey换取open token(open token 24小时有效期,应该每天获取两次比较保险),并且写入cookies.list。 目前还不清楚wskey有效期,所以在第二步中wskey过期及时发送通知以便重新上传wskey。

经过一下午折腾,确实可行。但是看Zy143L大佬源码发现genToken接口中的签名是请求coding.net返回的(应该是大佬自建部署的吧!)!这个动作引起了我的好奇心,没有公布签名算法,究竟是何等高级的玩意儿?请求没有任何数据,就一个get请求即可返回所有签名参数,难道签名参与计算的值都是固定的吗?

这类资料比较少,不过还是有乐于分享的大佬。找到几篇文章:

JD app sign 加密参数破解 - unidbg 逆向工具之unidbg(在pc端模拟执行so文件中的函数) Unidbg使用指南(一) Unidbg模拟执行大厂so实操教程(一) 先把框架搭起来 Unidbg模拟执行大厂so实操教程(二) 借鸡生蛋之SandHook的使用(一) 借鸡生蛋之某电商App签名so的使用(二) 借鸡生蛋之某电商App签名so的使用(三)

对于我来说难度比较大,涉及知识面广,不过如果有信心那就没问题。

我总结了一下经过步骤:

首先得搭建一个android开发环境。 下载京东安卓APP,apk文件可以当作压缩文件打开,从lib里面提取出libjdbitmapkit.so。 用ida修改libjdbitmapkit.so文件,需要修改一些地方屏蔽错误。 使用unidbg载入libjdbitmapkit.so,调用签名方法签名。 打包编译,部署到服务器。

步骤虽然简单,但是操作过程对于一个从来没有接触过andriod、汇编的人来说每一步都掉坑!!!

过大坑纪要

Exception in thread "main" java.lang.IllegalStateException: Illegal JNI version: 0xffffffff,这个问题坑了我好久,起初我以为自己环境有问题,因为根本找不到什么资料。其实就是so文件问题,如果你没有使用unidbg-0.9.1版本的话会出现这个错误,引发错误的根本原因就是先按借鸡生蛋之某电商App签名so的使用(三)对so文件打补丁,如果按上文推荐的文章顺序阅读那么就会被卡住,建议全部读一遍后再动手。 error-1.png

打补丁使用的ida软件,对于汇编是一脸懵逼的,借鸡生蛋之某电商App签名so的使用(三)一文中把修改位置写得很明白了,但是ida真心不会用,其中包括安装ida插件:

keystone中下载msi安装文件,Version 0.9.1有提供32位和64位装一个就行。安装过程一定要勾选安装pthon2.7,我就取消了,最后插件无法使用,还是自己手动去安装的,如果你也是手动安装记得安装six模块:pip install six。

把keypatch代码复制,到ida plugins目录建一个新文件Keypatch.py,把代码复制进去。

ida中Options-General-Disassembly-Number of opcode bytes设置成16,以十六进制显示字节码: ida-option.png

使用keypatch插件修改sourceDir部分,JNI_OnLoad部分使用ida功能Edit-Patch program-Change byte就好了。000037CC 04 25 C0 F2 01 05 MOVS R5, #0x10004这段我是用keypatch一直报错,浪费一两个小时,使用Patch program替换中间字节就行了: ida-patch-bytes.png 修改摘要:

00002D08 40 F0 40 81 BNE.W loc_2F8C -> 00002D08 40 E1 B loc_2F8C 00002F8C 6F F0 02 00 MOV R0, #0xFFFFFFFD -> 00002F8C 4F F0 01 00 MOV.W R0, \#1 000037C8 FF F7 96 F9 BL check_status -> 000037C8 FF F7 96 F9 BL check_status 000037C8 FF F7 96 F9 BL check_status 000037CC 80 46 MOV R8, R0 000037CE 00 28 CMP R0, \#0 000037D0 67 D1 BNE loc_38A2 -> 000037C8 FF F7 96 F9 BL check_status 000037CC 04 25 C0 F2 01 05 MOVS R5, #0x10004 000037D2 2A E0 B loc_382A;

打包一定要看Unidbg使用指南(一),所有操作都是在test文件夹完成的,不然会缺少包,装又装不上!打包也要载入test文件夹!

搭建服务

如果你能坚持到这步就已经成功了,只要能获取到签名参数想怎么玩就怎么玩!我还是用http服务比较顺手,网上找了个java socket代码一顿改!!!

在JD app sign 加密参数破解 - unidbg文中代码基础上修改,加入socket模拟http:

... public static void service(final Socket socket, final JingDong jd) { InputStream inSocket; try { //获取HTTP请求头 inSocket = socket.getInputStream(); int size = inSocket.available(); byte[] buffer = new byte[size]; inSocket.read(buffer); String request = new String(buffer); System.out.println("ClientBrowser:\n" + request + "\n" + "------------------------------------------------------------------"); String httpCode = "200 OK"; String result = ""; if (request.length() > 0) { try { JSONObject data = JSON.parseObject(request.substring(request.lastIndexOf("\r\n")).trim()); if (data != null) { String uuid = data.getString("uuid"); String functionId = data.getString("functionId"); try { String body = new URLCodec().decode(data.getString("body")); String version = data.getString("version"); if (!uuid.isEmpty() && !functionId.isEmpty() && !body.isEmpty() && !version.isEmpty()) { result = convert(jd.runJni(uuid, functionId, body, version)); System.out.println(result); System.out.println(convert(result)); } } catch (DecoderException e) { e.printStackTrace(); } } } catch (JSONException e) { e.printStackTrace(); result = "{\"code\": \"400\", \"message\": \"Invalid param\"}"; httpCode = "400 Bad Request"; } } //将响应头发送给客户端 String responseFirstLine = "HTTP/1.1 " + httpCode + "\r\n"; String responseHead = "Content-Type:application/json\r\n"; OutputStream outSocket = socket.getOutputStream(); System.out.println("ServerResponse:\n" + responseFirstLine + "\n" + responseHead + "\n" + result + "\n" + "--------------------------------------------------------------------"); System.out.println("current thread:" + Thread.currentThread().getName()); System.out.println("threads count:" + Thread.activeCount()); outSocket.write(responseFirstLine.getBytes()); outSocket.write(responseHead.getBytes()); outSocket.write("\r\n".getBytes()); outSocket.write(result.getBytes()); outSocket.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException { //獲取參數,服務監聽地址和端口 //java -Daddress=0.0.0.0 -Dport=9999 -jar unidbg-android.jar address = System.getProperty("address", "127.0.0.1"); port = Integer.parseInt(System.getProperty("port", "8668")); threadCount = Integer.parseInt(System.getProperty("thread", "10")); //綫程池 ExecutorService executorService = Executors.newFixedThreadPool(threadCount); final ServerSocket serverSocket; final JingDong jd = new JingDong(); try { //建立服务器Socket,监听客户端请求 serverSocket = new ServerSocket(port, 50, InetAddress.getByName(address)); System.out.println("Server is running on " + serverSocket.getLocalSocketAddress()); //死循环不间断监听客户端请求 while (true) { final Socket socket = serverSocket.accept(); System.out.println("biuld a new tcp link with client,the cient address:" + socket.getInetAddress() + ":" + socket.getPort()); //并发处理HTTP客户端请求 executorService.execute(new Runnable() { @Override public void run() { service(socket, jd); } }); } } catch (Exception e) { e.printStackTrace(); } finally { jd.destroy(); executorService.shutdown(); } } ...

网上七拼八凑折腾出来这么个玩意,使用:

java -Daddress=127.0.0.1 -Dport=8668 -DthreadCount=5 -jar xxx.jar

address:绑定地址,port:绑定端口,threadCount:线城数量。

请求参数:

//没有限制请求方法,GET POST随意,以下是请求body {"uuid":"随机字符串,随机生成就行了","functionId":"接口(genToken)","body":"URI编码的json,不要带任何多余东西","version":"安卓版本"} //返回结果 { "uuid": "9d53afe389f6ae5f", "st": "1630922926986", "sign": "0a9b72a4fc6ffd5d799642dd295623f8", "sv": "111" }

然后外面再套一层nginx反代,或者和自己jd_script同服务器,内网请求。你要是绑定0.0.0.0暴漏出去也行!

结语

为了个签名折腾了好久,整个签名使用了functionId,body,uuid,client,clientVersion五个参数,client基本可以固定android了。使用ida汇编长了不少知识。

至此应该是不用在操心京东登陆方面的问题了,只要使用京东APP全部都自动更新了!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3