IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 猿人学安卓逆向对抗比赛(1-5题) -> 正文阅读

[移动开发]猿人学安卓逆向对抗比赛(1-5题)

题目大纲

题号题目
java层加密
so层加密
so层加密带混淆
grpc
双向认证
设备指纹加密
quic
upx
tcp
tls

第一题:java层加密

不管三七二十一,上来先抓个包看看
在这里插入图片描述

这是一个post请求,并且附带三个参数,其中一个是sign,那么第一题要分析的应该就是这个sign了,用jadx反编译apk,尝试搜索一下请求地址【/app1】

在这里插入图片描述

只有一个结果,就是你了。选中对应的调用方法,右键选择【查找用例】

在这里插入图片描述
在这里插入图片描述
这次有两个结果,随便点其中一个

在这里插入图片描述

这里看到了sign的关键词,猜测这个就是生成sign的函数,尝试使用frida hook一下对比结果

		var Sign = Java.use("com.yuanrenxue.match2022.security.Sign");
		Sign.sign.implementation = function(a){
			console.log('on sign');
			send('a');
			send(a);
			var sign = this.sign(a);
			send('sign');
			send(sign);
			console.log('le sign');
			return sign;
		};

抓包结果

page=1

sign=e17b4693d730d180b6f6c523a427c08e

t=1652709242

hook结果

on sign
a
长  度:16
字节集:[112, 97, 103, 101, 61, 49, 49, 54, 53, 50, 55, 48, 57, 50, 52, 50]
字符串:b'page=11652709242'
Base64:cGFnZT0xMTY1MjcwOTI0Mg==
HEX   :706167653d3131363532373039323432
sign
e17b4693d730d180b6f6c523a427c08e
le sign

可以看到sign的结果是对的上的,入参就是页数加上时间戳,与请求的参数也是可以对上的,那么sign的计算就是这个函数。

如果把这个函数改成python,那是比较麻烦的,那这了就直接用java运行了,把代码复制到新的文件,复制后【OooO00o.OooO00o(-592855683721616730L)】这个地方会报错,hook一下很容易就知道是【“%02x%02x%02x%02x”】,后面这个相关的就都跳过了。

然后编写一个测试的main方法

在这里插入图片描述

运行的结果是【b94231f3390145e577ab667354a2395d】,神了个奇了,居然与hook的结果不一样。代码里面都是正常的运算代码,也没有设置到时间和随机数这些变量,怎么会不一样呢?

这里就要考虑有没有可能是反编译出了问题,那这里函数不多,那就以函数为单位测试一下哪里出错了

在这里插入图片描述
第一步运行的是padding函数,那么就hook一下看看

		Sign.padding.implementation = function(bArr){
			console.log('on padding');
			let ret = this.padding(bArr);
			console.log(ret);
			console.log('le padding');
			return ret;
		};

可以得到

on sign
a
长  度:16
字节集:[112, 97, 103, 101, 61, 49, 49, 54, 53, 50, 55, 56, 56, 52, 48, 50]
字符串:b'page=11652788402'
Base64:cGFnZT0xMTY1Mjc4ODQwMg==
HEX   :706167653d3131363532373838343032
on padding
[112, 97, 103, 101, 61, 49, 49, 54, 53, 50, 55, 56, 56, 52, 48, 50, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0]
le padding
sign
8fd84d9c105338c8b4872a67b2cca259
le sign

然后在java中也打印一下这个结果

在这里插入图片描述
发现结果已经不一致了

在这里插入图片描述
这个函数看名字应该是一个填充的函数,在整个函数体下来看应该是一个哈希算法的填充

在这里插入图片描述
这里的最后8个字节就是来自于这个循环,实际就是内容长度的序列化数据,但是java代码这里指定的数据类型是int,但是8个字节的应该是long类型,所以这里序列化的时候用了两个int来填充,导致数据不一样,所以把【length】的类型从int修改为long

再次运行后发现,运行结果就与hook结果一致了。代码就扣出来,那么要怎么给python进行调用呢?

某帮m3u8解密算法分析这篇文章的回复中,我提到了如何在python中使用jpype库调用java代码,所以这里也用相同的方法调用

import java.util.ArrayList;

public class Sign {
    public static void main(String[] args) {
        System.out.println(new Sign().sign("page=11652788402".getBytes()));
    }

    private static final int A = 1732584193;
    private static final int B = -271733879;
    private static final int C = -1732584194;
    private static final int D = 271733878;

    private static int f(int i, int i2, int i3) {
        return ((~i) & i3) | (i2 & i);
    }

    private static int ff(int i, int i2, int i3, int i4, int i5, int i6) {
        return rotateLeft(i + f(i2, i3, i4) + i5, i6);
    }

    private static int g(int i, int i2, int i3) {
        return (i & i3) | (i & i2) | (i2 & i3);
    }

    private static int gg(int i, int i2, int i3, int i4, int i5, int i6) {
        return rotateLeft(i + g(i2, i3, i4) + i5 + 1518565785, i6);
    }

    private static int h(int i, int i2, int i3) {
        return (i ^ i2) ^ i3;
    }

    private static int hh(int i, int i2, int i3, int i4, int i5, int i6) {
        return rotateLeft(i + h(i2, i3, i4) + i5 + 1859775393, i6);
    }

    private ArrayList<Integer> padding(byte[] bArr) {
        long length = bArr.length * 8;
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (byte b : bArr) {
            arrayList.add(Integer.valueOf(b));
        }
        arrayList.add(128);
        while (((arrayList.size() * 8) + 64) % 512 != 0) {
            arrayList.add(0);
        }
        for (int i = 0; i < 8; i++) {
            arrayList.add(Integer.valueOf((int) ((length >>> (i * 8)) & 255)));
        }
        return arrayList;
    }

    private static int rotateLeft(int i, int i2) {
        return (i >>> (32 - i2)) | (i << i2);
    }

    public String sign(byte[] bArr) {
        ArrayList<Integer> padding = padding(bArr);
        int i = A;
        int i2 = B;
        int i3 = C;
        int i4 = D;
        for (int i5 = 0; i5 < padding.size() / 64; i5++) {
            int[] iArr = new int[16];
            for (int i6 = 0; i6 < 16; i6++) {
                int i7 = (i5 * 64) + (i6 * 4);
                iArr[i6] = (padding.get(i7 + 3).intValue() << 24) | padding.get(i7).intValue() | (padding.get(i7 + 1).intValue() << 8) | (padding.get(i7 + 2).intValue() << 16);
            }
            int[] iArr2 = {0, 4, 8, 12};
            int i8 = i;
            int i9 = i2;
            int i10 = i3;
            int i11 = i4;
            int i12 = 0;
            while (i12 < 4) {
                int i13 = iArr2[i12];
                i8 = ff(i8, i9, i10, i11, iArr[i13], 3);
                int ff = ff(i11, i8, i9, i10, iArr[i13 + 1], 7);
                i10 = ff(i10, ff, i8, i9, iArr[i13 + 2], 11);
                i9 = ff(i9, i10, ff, i8, iArr[i13 + 3], 19);
                i12++;
                i11 = ff;
            }
            int[] iArr3 = {0, 1, 2, 3};
            int i14 = i8;
            int i15 = i11;
            for (int i16 = 0; i16 < 4; i16++) {
                int i17 = iArr3[i16];
                i14 = gg(i14, i9, i10, i15, iArr[i17], 3);
                i15 = gg(i15, i14, i9, i10, iArr[i17 + 4], 5);
                i10 = gg(i10, i15, i14, i9, iArr[i17 + 8], 9);
                i9 = gg(i9, i10, i15, i14, iArr[i17 + 12], 13);
            }
            int[] iArr4 = {0, 2, 1, 3};
            int i18 = i14;
            int i19 = 0;
            while (i19 < 4) {
                int i20 = iArr4[i19];
                int hh = hh(i18, i9, i10, i15, iArr[i20], 3);
                i15 = hh(i15, hh, i9, i10, iArr[i20 + 8], 9);
                i10 = hh(i10, i15, hh, i9, iArr[i20 + 4], 11);
                i9 = hh(i9, i10, i15, hh, iArr[i20 + 12], 15);
                i19++;
                i18 = hh;
            }
            i += i18;
            i2 += i9;
            i3 += i10;
            i4 += i15;
        }
        return String.format("%02x%02x%02x%02x", Integer.valueOf(i), Integer.valueOf(i2), Integer.valueOf(i3), Integer.valueOf(i4));
    }
}

把上面代码保存到Sign.java

javac Sign.java
jar cvf Sign.jar Sign.class

运行上面两个命令行后,可以得到Sign.jar,复制到python同目录下,使用前需要先安装依赖

pip install JPype1
import jpype
import requests
import time

def main():
    jpype.startJVM(jpype.getDefaultJVMPath(), "-ea", "-Djava.class.path=Sign.jar")  # 启动java虚拟机
    jclass = jpype.JClass("Sign")  # 获取java类
    Sign = jclass()  # 实例化java对象
    url = 'https://appmatch.yuanrenxue.com/app1'
    for page in range(1, 6):
        data = {
            "page": page,
            "t": int(time.time())
        }
        data["sign"] = str(Sign.sign(f'page={data["page"]}{data["t"]}'.encode()))
        response = requests.post(url, data=data).json()
        print(response)
    jpype.shutdownJVM()

if __name__ == '__main__':
    main()

在这里插入图片描述

第二题:so层加密

通过第一题可以知道,大部分的题目窗口都在【com.yuanrenxue.match2022.fragment.challenge】这个类下面在这里插入图片描述
第二题中加载了so,并且只有一个sign函数是native函数,那么就hook这个函数,同时抓包查看

在这里插入图片描述
在这里插入图片描述

on sign 
str
1:1652965963
sign
IB/wL5GD01zlBS3MvRTLjw==
le sign

可以看到请求体与hook的数据是一致的,但是接着就不分析so了,直接上unidbg工具,从https://github.com/zhkl0228/unidbg拉取项目到本地,拉取完成后可以尝试运行一下看雪so的测试类【com.kanxue.test2.MainActivity】

在这里插入图片描述
没有出现报错,并且出现【Found: XuE】的字样,表示环境正常,开始编写我们自己的类【com.yuanrenxue.match2022.fragment.challenge.ChallengeTwoFragment】

package com.yuanrenxue.match2022.fragment.challenge;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.StringObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;

import java.io.File;
import java.io.IOException;

public class ChallengeTwoFragment {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;
    private final DvmClass ChallengeTwoFragment;
    private final boolean logging;

    public ChallengeTwoFragment(boolean logging) {
        this.logging = logging;
        emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.yuanrenxue.match2022").build();
        final Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File("../yuanrenxuem106.apk"));
        vm.setVerbose(logging);
        DalvikModule dm = vm.loadLibrary("match02", true);
        dm.callJNI_OnLoad(emulator);
        module = dm.getModule();
        ChallengeTwoFragment = vm.resolveClass("com/yuanrenxue/match2022/fragment/challenge/ChallengeTwoFragment");
    }

    public void destroy() throws IOException {
        emulator.close();
    }

    public static void main(String[] args) throws Exception {
        ChallengeTwoFragment challengeTwoFragment = new ChallengeTwoFragment(false);
        System.out.println(challengeTwoFragment.sign("1:1652965963"));
        challengeTwoFragment.destroy();
    }

    public String sign(String str) {
        StringObject sign = ChallengeTwoFragment.newObject(null).callJniMethodObject(
                emulator,
                "sign(Ljava/lang/String;)Ljava/lang/String;",
                vm.addLocalObject(new StringObject(vm, str))
        );
        return sign.getValue();
    }
}

在这里插入图片描述
测试结果与hook结果是一致的,大功告成,接下在就是怎么给到python调用了。一种是可以在java起一个本地服务端,然后通过http调用。另一种是像第一题一样,打包成jar给到python调用,那么我这里用的是后后面一种方法。

根据【JAVA】使用intellij IDEA将项目打包为jar包的提示,按照步骤把【unidbg-android】这整个项目打包成jar

在这里插入图片描述
打包后文件数量比较多,是正常现象,最后一个就是我们打包出来的jar,把全部文件都复制到python的目录

在这里插入图片描述
我的目录结构如上图,那么接着就是根据上一题一样,直接调用生成sign就可以,没有什么特别的

import jpype
import requests
import time

def main():
    jpype.startJVM(jpype.getDefaultJVMPath(), "-ea", "-Djava.class.path=../unidbg-android.jar")
    jclass = jpype.JPackage("com.yuanrenxue.match2022.fragment.challenge").ChallengeTwoFragment
    ChallengeTwoFragment = jclass(False)
    url = 'https://appmatch.yuanrenxue.com/app2'
    for page in range(1, 6):
        data = {
            "page": page,
            "ts": int(time.time())
        }
        data["sign"] = str(ChallengeTwoFragment.sign(f'{data["page"]}:{data["ts"]}'.encode()))
        response = requests.post(url, data=data).json()
        print(response)
    jpype.shutdownJVM()

if __name__ == '__main__':
    main()

在这里插入图片描述

第三题:so层加密带混淆

在这里插入图片描述

第三题和第二题基本相似,hook native方法,然后抓包

在这里插入图片描述

on crypto
str
0011652967032000
j
1652967032000
crypto
177f3b597937c04a0f4fccc208de9fce0ce39e5764c4115754b1cd643d528898
le crypto

hook结果与抓包结果也是对的上的,接下来一样是用unidbg生成签名

package com.yuanrenxue.match2022.fragment.challenge;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.StringObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;

import java.io.File;
import java.io.IOException;

public class ChallengeThreeFragment {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;
    private final DvmClass ChallengeThreeFragment;
    private final boolean logging;

    public ChallengeThreeFragment(boolean logging) {
        this.logging = logging;
        emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.yuanrenxue.match2022").build();
        final Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File("../yuanrenxuem106.apk"));
        vm.setVerbose(logging);
        DalvikModule dm = vm.loadLibrary("match03", true);
        dm.callJNI_OnLoad(emulator);
        module = dm.getModule();
        ChallengeThreeFragment = vm.resolveClass("com/yuanrenxue/match2022/fragment/challenge/ChallengeThreeFragment");
    }

    public void destroy() throws IOException {
        emulator.close();
    }

    public static void main(String[] args) throws Exception {
        ChallengeThreeFragment ChallengeThreeFragment = new ChallengeThreeFragment(true);
        System.out.println(ChallengeThreeFragment.sign("0011652967032000", "1652967032000"));;
        ChallengeThreeFragment.destroy();
    }

    public String sign(String str, String j) {
        StringObject sign = ChallengeThreeFragment.newObject(null).callJniMethodObject(
                emulator,
                "crypto(Ljava/lang/String;J)Ljava/lang/String;",
                vm.addLocalObject(new StringObject(vm, str)),
                Long.parseLong(j)
        );
        return sign.getValue();
    }
}

虽然每次运行的结果都不一样,可能是有随机数造成的。打包过去试试请求

import jpype
import requests
import time

def main():
    jpype.startJVM(jpype.getDefaultJVMPath(), "-ea", "-Djava.class.path=../unidbg-android.jar")
    jclass = jpype.JPackage("com.yuanrenxue.match2022.fragment.challenge").ChallengeThreeFragment
    ChallengeThreeFragment = jclass(False)
    url = 'https://appmatch.yuanrenxue.com/app3'
    for page in range(1, 6):
        tim = str(int(time.time() * 1000))
        data = {
            "page": page
        }
        data["m"] = str(ChallengeThreeFragment.sign(str(data['page']).zfill(3) + tim, tim))
        response = requests.post(url, data=data).json()
        print(response)
    jpype.shutdownJVM()

if __name__ == '__main__':
    main()

在这里插入图片描述
可以请求成功,说明也是没有问题的

第四题:grpc

第四题是grpc,那么grpc是什么呢?

更多人可能接触的更多的是基于REST的通信。我们已经看到,REST 是一种灵活的体系结构样式,它定义了对实体资源的基于CRUD的操作。 客户端使用请求/响应通信模型跨 HTTP 与资源进行交互。尽管 REST 是广泛实现的,但一种较新的通信技术gRPC已在各个生态中获得巨大的动力。

gRPC,其实就是RPC框架的一种,前面带了一个g,代表是RPC中的大哥,龙头老大的意思,另外g也有global的意思,意思是全球化比较fashion,是一个高性能、开源和通用的 RPC 框架,基于ProtoBuf(Protocol Buffers) 序列化协议开发,且支持众多开发语言。面向服务端和移动端,基于 HTTP/2 设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。

在这里插入图片描述
既然说到protobuf,就不得不说以前的一篇文章【JS逆向系列】某方数据获取,proto入门,我做了这道题才知道,原来这就是grpc呀,那么某方数据用的就是grpc,没有看前面文章的建议先看看再回来看这道题

在这里插入图片描述
接下来一样是抓包和hook

在这里插入图片描述

on sign
str
1:1652969194488
j
1652969194488
sign
8faf8d3081d431db
le sign

因为pbf是序列化后的数据,但是依稀能够看到最后一段签名的对的上的,首先第一步就是调用so生成sign

package com.yuanrenxue.match2022.fragment.challenge;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.StringObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;

import java.io.File;
import java.io.IOException;

public class ChallengeFourFragment {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;
    private final DvmClass ChallengeFourFragment;
    private final boolean logging;

    public ChallengeFourFragment(boolean logging) {
        this.logging = logging;
        emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.yuanrenxue.match2022").build();
        final Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File("../yuanrenxuem106.apk"));
        vm.setVerbose(logging);
        DalvikModule dm = vm.loadLibrary("match04", true);
        dm.callJNI_OnLoad(emulator);
        module = dm.getModule();
        ChallengeFourFragment = vm.resolveClass("com/yuanrenxue/match2022/fragment/challenge/ChallengeFourFragment");
    }


    public void destroy() throws IOException {
        emulator.close();
    }

    public static void main(String[] args) throws Exception {
        ChallengeFourFragment ChallengeFourFragment = new ChallengeFourFragment(true);
        System.out.println(ChallengeFourFragment.sign("1:1652969194488", "1652969194488"));;
        ChallengeFourFragment.destroy();
    }

    public String sign(String str, String j) {
        StringObject sign = ChallengeFourFragment.newObject(null).callJniMethodObject(
                emulator,
                "sign(Ljava/lang/String;J)Ljava/lang/String;",
                vm.addLocalObject(new StringObject(vm, str)),
                Long.parseLong(j)
        );
        return sign.getValue();
    }
}

这次运行结果就是一致的,然后就可以打包给python使用了。第二步就是编写proto文件,因为java层的代码是被混淆的,不好找出原本的键名,不过proro对键名是不敏感的,那么就以简单为主,按照前面的参数来自定义键名,首先尝试编写请求体参数

需要先安装依赖

pip install grpcio
pip install protobuf
pip install grpcio-tools

根据抓包的链接【http://180.76.60.244:9901/challenge.Challenge/SayHello】创建proto文件

在这里插入图片描述
这三个地方需要注意,要与抓包的参数对应

syntax = "proto2";

// http://180.76.60.244:9901/challenge.Challenge/SayHello
// python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. app_proto2.proto

package challenge;

message RequestMessage {
	optional int32 page = 1;
	optional int64 t = 2;
	optional string sign = 3;
}

message ResponseMessage {
		repeated Item data = 1;
}

message Item {
	optional string value = 1;
}

service Challenge{
 rpc SayHello (RequestMessage) returns (ResponseMessage) {}
}

上面文件保存为【app_proto2.proto】,放到python文件同目录,然后执行下面指令

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. app_proto2.proto

然后就可以得到【app_proto2_pb2.py】和【app_proto2_pb2_grpc.py】

在这里插入图片描述
根据其他的文章说明,新建Stub之前,需要先创建Channel

channel = grpc.insecure_channel('180.76.60.244:9901')

在这里插入图片描述
然后创建client用于真正的PRC请求

client = app_proto2_pb2_grpc.ChallengeStub(channel=channel)

测试发出请求


import app_proto2_pb2
import app_proto2_pb2_grpc
import jpype
import time
import grpc

def main():
    channel = grpc.insecure_channel('180.76.60.244:9901')
    client = app_proto2_pb2_grpc.ChallengeStub(channel=channel)
    jpype.startJVM(jpype.getDefaultJVMPath(), "-ea", "-Djava.class.path=../unidbg-android.jar")
    jclass = jpype.JPackage("com.yuanrenxue.match2022.fragment.challenge").ChallengeFourFragment
    ChallengeFourFragment = jclass(False)
    requests_data = app_proto2_pb2.RequestMessage()
    requests_data.page = 1
    requests_data.t = int(time.time() * 1000)
    requests_data.sign = str(ChallengeFourFragment.sign(str(requests_data.page) + ':' + str(requests_data.t), str(requests_data.t)))
    print(requests_data)
    response = client.SayHello(requests_data)
    print(response)
    jpype.shutdownJVM()


if __name__ == '__main__':
    main()

在这里插入图片描述
请求数据正常,完成

本题参考文献
1.【后台技术】gRPC 基础概念详解
2.gRPC传输协议使用(python教程)
3.猿人学-app逆向比赛第四题grpc题解

第五题:双向认证

既然题目已经告我我们是双向认证了,那么直接抓包肯定是不用想了,必然抓不到,这里我就直接用r0ysue的r0capture,这个frida脚本同时可以把客户端证书dump出来,项目地址:https://github.com/r0ysue/r0capture

在这里插入图片描述
这里项目拉取下来后,我修改了小小东西,我把证书dump出来的目录改到了对应app的目录下,避免一些权限问题

在这里插入图片描述
可以看到上面证书已经被dump出来,dump后的密码为【r0ysue】,同时也抓到包了,可以看到请求地址,请求头和请求体

把dump到手机的证书传到电脑上,我顺便改名为【yuanrenxue.p12】接着使用【OpenSSL】库来提取出key和cert

from OpenSSL import crypto

p12 = crypto.load_pkcs12(open("yuanrenxue.p12", 'rb').read(), b'r0ysue')
with open('client.key', 'wb') as f:
    f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, p12.get_privatekey()))
with open('client.cert', 'wb') as f:
    f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, p12.get_certificate()))

然后可以获得【client.key】和【client.cert】,接着试试发送请求

from OpenSSL import crypto
import requests

def main():
    # p12 = crypto.load_pkcs12(open("yuanrenxue.p12", 'rb').read(), b'r0ysue')
    # with open('client.key', 'wb') as f:
    #     f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, p12.get_privatekey()))
    # with open('client.cert', 'wb') as f:
    #     f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, p12.get_certificate()))

    url = 'https://180.76.60.244:18443/api/app5'
    for page in range(1, 6):
        data = {
            'page': page
        }
        response = requests.post(url, data=data, verify=False, cert=('client.cert', 'client.key'))
        print(response.text)

if __name__ == '__main__':
    main()

在这里插入图片描述
可以看到已经正确获取数据了,到这里就结束了。

前面不是说有十道题,怎么一半就没了呢?主要是因为菜,后面的做不出来

更多内容可以加入我的星球学习

在这里插入图片描述

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-05-24 18:20:14  更:2022-05-24 18:22:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 0:54:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码