用户名
密码

折腾京东APP签名-获取wskey相关签名参数

orzlee
2021-09-06 / 6 评论 / 560 阅读 / 正在检测是否收录...

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的方法。

我的思路:

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

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

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

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

我总结了一下经过步骤:

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

步骤虽然简单,但是操作过程对于一个从来没有接触过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插件:

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

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

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

    4. 使用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全部都自动更新了!

0
取消
扫码打赏
支付金额随意哦!

评论 (6)

取消
  1. 头像
    DJD
    Windows 10 · Google Chrome

    大佬,Convert method 代码可以贴而一下吗

    回复
  2. 头像
    KIKI
    MacOS · Google Chrome

    能否透露下app版本..我拿10.X的改..ida里面改完了..但是实际还是会报错

    回复
    1. 头像
      orzlee 作者
      Linux · Google Chrome
      @ KIKI

      我都是下最新版本

      回复
  3. 头像
    ee
    Windows 10 · Google Chrome

    最新的版本10.1.6 有新的变化了吗?怎么总是通不过

    回复
    1. 头像
      orzlee 作者
      Linux · Google Chrome
      @ ee

      什么东西版本10.1.6?

      回复
  4. 头像
    sos
    Windows 10 · Google Chrome

    大佬,可以给份服务器源码吗

    回复