漁滒 发表于 2022-5-20 19:25

猿人学安卓逆向对抗比赛(1-5题)

@(猿人学安卓逆向对抗比赛(1-5题))

## 题目大纲
|题号| 题目 |
|--|--|
| 一 | java层加密 |
| 二 | so层加密 |
| 三 | so层加密带混淆 |
| 四 | grpc |
| 五 | 双向认证 |
| 六 | 设备指纹加密 |
| 七 | quic |
| 八| upx |
| 九| tcp |
| 十 | tls |


## 第一题:java层加密
不管三七二十一,上来先抓个包看看


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



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



这次有两个结果,随便点其中一个



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

```javascript
                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;
                };
```

抓包结果

```javascript
page=1

sign=e17b4693d730d180b6f6c523a427c08e

t=1652709242
```

hook结果

```javascript
on sign
a
长度:16
字节集:
字符串: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一下看看

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

可以得到

```javascript
on sign
a
长度:16
字节集:
字符串:b'page=11652788402'
Base64:cGFnZT0xMTY1Mjc4ODQwMg==
HEX   :706167653d3131363532373838343032
on padding

le padding
sign
8fd84d9c105338c8b4872a67b2cca259
le sign
```
然后在java中也打印一下这个结果


发现结果已经不一致了


这个函数看名字应该是一个填充的函数,在整个函数体下来看应该是一个哈希算法的填充


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

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

在[某帮m3u8解密算法分析](https://www.52pojie.cn/thread-1342242-1-1.html)这篇文章的回复中,我提到了如何在python中使用jpype库调用java代码,所以这里也用相同的方法调用

```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;
            for (int i6 = 0; i6 < 16; i6++) {
                int i7 = (i5 * 64) + (i6 * 4);
                iArr = (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;
                i8 = ff(i8, i9, i10, i11, iArr, 3);
                int ff = ff(i11, i8, i9, i10, iArr, 7);
                i10 = ff(i10, ff, i8, i9, iArr, 11);
                i9 = ff(i9, i10, ff, i8, iArr, 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;
                i14 = gg(i14, i9, i10, i15, iArr, 3);
                i15 = gg(i15, i14, i9, i10, iArr, 5);
                i10 = gg(i10, i15, i14, i9, iArr, 9);
                i9 = gg(i9, i10, i15, i14, iArr, 13);
            }
            int[] iArr4 = {0, 2, 1, 3};
            int i18 = i14;
            int i19 = 0;
            while (i19 < 4) {
                int i20 = iArr4;
                int hh = hh(i18, i9, i10, i15, iArr, 3);
                i15 = hh(i15, hh, i9, i10, iArr, 9);
                i10 = hh(i10, i15, hh, i9, iArr, 11);
                i9 = hh(i9, i10, i15, hh, iArr, 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

```bash
javac Sign.java
```

```bash
jar cvf Sign.jar Sign.class
```

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

```bash
pip install JPype1
```

```python
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这个函数,同时抓包查看





```javascript
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】

```java
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包](https://blog.csdn.net/ET1131429439/article/details/119907638)的提示,按照步骤把【unidbg-android】这整个项目打包成jar


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


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

```python
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方法,然后抓包



```javascript
on crypto
str
0011652967032000
j
1652967032000
crypto
177f3b597937c04a0f4fccc208de9fce0ce39e5764c4115754b1cd643d528898
le crypto
```

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

```java
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();
    }
}

```

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

```python
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入门](https://www.52pojie.cn/thread-1611798-1-1.html),我做了这道题才知道,原来这就是grpc呀,那么某方数据用的就是grpc,没有看前面文章的建议先看看再回来看这道题


接下来一样是抓包和hook



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

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

```java
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对键名是不敏感的,那么就以简单为主,按照前面的参数来自定义键名,首先尝试编写请求体参数

需要先安装依赖

```bash
pip install grpcio
pip install protobuf
pip install grpcio-tools
```

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


这三个地方需要注意,要与抓包的参数对应


```javascript
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文件同目录,然后执行下面指令

```bash
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

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


然后创建client用于真正的PRC请求

```python
client = app_proto2_pb2_grpc.ChallengeStub(channel=channel)
```

测试发出请求

```python

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()

```


请求数据正常,完成

本题参考文献
(https://weibo.com/ttarticle/p/show?id=2309404656237123076331)
(https://blog.csdn.net/luanpeng825485697/article/details/81033368)
(https://mp.weixin.qq.com/s/TaLeKFc6tjd6_eaG2VONlA)


## 第五题:双向认证

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


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


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

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

```python
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】,接着试试发送请求

```python
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()
```


可以看到已经正确获取数据了,到这里就结束了。

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

漁滒 发表于 2022-5-24 11:37

iamspiderman 发表于 2022-5-24 11:36
大佬,请问你用的什么抓包工具呀第一题

小黄鸟HttpCanary

漁滒 发表于 2022-6-17 16:05

Eb0ny 发表于 2022-6-17 16:00
想问下大佬,第二题我做到unidbg的时候按照你的java脚本运行会什么返回的sign是null,是环境的原因还是啥

内存读写报错了,一般是代码有问题

foolboy 发表于 2022-5-20 19:27

鱼总流弊

kiopc 发表于 2022-5-20 19:30

先说一声牛beer

wasm2023 发表于 2022-5-20 20:02

渔滒太强了

秋澄 发表于 2022-5-20 20:32

别啊,渔总快更,等着看后面的呢

lovehfs 发表于 2022-5-20 20:51

好好学习一下

10086abc 发表于 2022-5-20 21:04

大佬大佬

taxuewuhen 发表于 2022-5-20 21:35

谢谢分享

BoBuo 发表于 2022-5-20 22:48

向渔滒学习{:1_932:}

ZYZZMD 发表于 2022-5-21 07:06

太牛了。。
页: [1] 2 3 4 5 6 7 8 9
查看完整版本: 猿人学安卓逆向对抗比赛(1-5题)