我是靠谱客的博主 狂野小熊猫,最近开发中收集的这篇文章主要介绍逆向爬虫35 常见加密的python实现逆向爬虫35 常见加密的python实现,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

逆向爬虫35 常见加密的python实现

目的:将App逆向中常见的算法用python实现,方便以后的使用

Java常见加密

隐藏字符串

  • 字符串是以字节编码数组的形式存储或网络传输的,因此可以通过指定编码的字节数组来代替字符串,起到隐藏字符串的目的。
  • Java中的字节是有符号数,范围是-128 ~ 127,python中的字节是无符号数,范围是0 ~ 255,因此想要把java中的字节数组写到python中,需要进行转化
  • 下面是在java中将字符串转化为字节数组的示例,可以看到java中的字节有正有负
import java.util.Arrays;

public class Hello {
    public static void main(String[] args) {
        String s1 = new String("一个小黑666");
        byte[] b1 = s1.getBytes();
        System.out.println(Arrays.toString(b1)); //[-28, -72, -128, -28, -72, -86, -27, -80, -113, -23, -69, -111, 54, 54, 54]
    }
}
  • 下面是在python中将字符串转化为字节数组的示例,可以看到python中的字节只有正,没有负
s1 = "一个小黑666"
byte_s1 = s1.encode()
list_s1 = [b for b in byte_s1]
print(list_s1)  # [228, 184, 128, 228, 184, 170, 229, 176, 143, 233, 187, 145, 54, 54, 54]
  • 下面是将java中的字节数组转换成python字节数组的代码
# 从java中得到的字节数组
byte_s1 = [-28, -72, -128, -28, -72, -86, -27, -80, -113, -23, -69, -111, 54, 54, 54]
byte_b2 = bytearray()	# 利用bytearray构造空的python字节数组
for b in byte_s1:
    b = b & 0xff
    byte_b2.append(b)
s2 = byte_b2.decode()
print(s2)	# 一个小黑666

注意事项

  • 使用的编码是 utf-8 还是 gbk
  • 一般用于加密哈希算法中的盐和对称加密AES的key和iv

uuid

  • uuid是一个随机id字符串,每一种语言中都会内置生成uuid的方法。
  • Java中生成uuid的方法
import java.util.UUID;

public class Hello {
    public static void main(String[] args) {
        String uuid = UUID.randomUUID().toString();
        System.out.println(uuid);   // ebcc80c4-7941-4c5f-9c86-47ace1f695f5
    }
}
  • Python中生成uuid的方法
import uuid

uid = str(uuid.uuid4())
print(uid)  # 8dd4aeb0-7a93-4fec-8c49-662af2ccfa77
  • uuid分为两种情况:

  • 第一种,每次请求,抓包都不一样 ——> 直接伪造生成

  • 第二种,抓包发现uuid固定,但app清除数据或重新卸载安装时,uuid发生变化

    ​ 这种情况的uuid可能是通过发送固定请求到服务求,由服务器返回的uuid,保存在App本地的xxx.xml文件中,该uuid作为之后手机请求服务器的凭证

  • 思路:

    1. 直接用固定uuid是否可行?
    2. 动态生成是否可行?
    3. 过程模拟,模拟获取uuid的请求获取uuid,带上服务器返回的uuid发送请求。

十六进制随机值

  • Java生成十六进制随机值的代码
import java.math.BigInteger;
import java.security.SecureRandom;

public class Hello {
    public static void main(String[] args) {
        // 随机生成80为0或1,10个字节
        BigInteger v1 = new BigInteger(80, new SecureRandom());
        // 让字节以16进制展示
        String res = v1.toString(16);
        System.out.println(res);    // 6a4c6fdfb797d5450c54
    }
}
  • Python生成十六进制随机值的代码
import random

data = random.randbytes(10)	# randombytes只在python3.9以后才有
randHex = "".join(['%02x' %(byte) for byte in data])
print(randHex)	# 3d4814c40e44a196df1c
  • 如果python不是3.9可以用以下版本
import random

byte_list = []
for i in range(10):
    byte_list.append(random.randint(0, 255))
res = "".join(["%02x" %(b) for b in byte_list])
print(res)	# 08e4461bf11a8631960c

时间戳

  • Java中生成时间戳的代码
public class Hello {
    public static void main(String[] args) {
        String t1 = String.valueOf(System.currentTimeMillis() / 1000);
        String t2 = String.valueOf(System.currentTimeMillis());
        System.out.println(t1);     // 1652538360
        System.out.println(t2);     // 1652538360408
    }
}
  • Python中生成时间戳的代码
import time

t1 = str(int(time.time()))
t2 = str(int(time.time()*1000))
print(t1)   # 1652538447
print(t2)   # 1652538447351

注意事项

  • 时间戳在请求头的参数里是以字符串形式出现的

  • 时间戳的意义:

    ​ 1.大厂会校验请求携带的时间戳

    ​ 2.时间戳会当作明文的一部分放进哈希算法进行加密,再根据哈希加密进行校验

md5 (其它哈希用法和md5类似)

  • Java中的md5代码 (含加盐)
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class Hello {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        String name = "一个小黑";
        MessageDigest instance = MessageDigest.getInstance("MD5");
        instance.update("salt".getBytes());     // 加盐
        byte[] nameBytes = instance.digest(name.getBytes());
        System.out.println(Arrays.toString(nameBytes)); // [32, 104, -25, 107, 106, -6, 40, 68, -127, 84, 99, -121, 7, 93, 8, -23]

        // 十六进制展示
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < nameBytes.length; i++) {
            int val = nameBytes[i] & 255;
            if (val < 16) {
                sb.append("0");
            }
            sb.append(Integer.toHexString(val));
        }
        String hexData = sb.toString();
        System.out.println(hexData);    // 2068e76b6afa284481546387075d08e9
    }
}
  • Python中的md5代码 (含加盐)
import hashlib

m = hashlib.md5()
m.update("salt".encode("utf-8"))    # 加盐
m.update("一个小黑".encode("utf-8"))

v1 = m.digest()
v2 = m.hexdigest()
print(v1)   # b' hxe7kjxfa(Dx81Tcx87x07]x08xe9'
print(v2)   # 2068e76b6afa284481546387075d08e9

注意事项

  • md5密文的特点是32位16进制数字

  • 获得密文后可能会进一步变大小写

  • 有盐的话需要找盐,最重要的是要找到md5的的明文

    1. 读取请求头/请求体/url 数据,某个字段

    2. 读取数据拼接字符串

      ​ name=apphao&age=18&size=20

      ​ nameapphaoage18size20

      ​ 固定值namewupeiqiage19size20固定值

  • 可能会拿到密文后,获取里面的固定几位,再添加到后面,导致密文超过32位

  • md5后期可以利用hook技术定位

AES

  • Java中的AES代码
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Hello {
    public static void main(String[] args) throws Exception {
        String data = "一个小黑";
        String key = "fd6b639dbcff0c2a1b03b389ec763c4b";
        String iv = "77b07a672d57d64c";

        // 加密
        byte[] raw = key.getBytes();
        SecretKeySpec sKeySpec = new SecretKeySpec(raw, "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, sKeySpec, ivSpec);
        byte[] encrypted = cipher.doFinal(data.getBytes());

        System.out.println(Arrays.toString(encrypted)); // [-91, -126, 100, 73, -87, -97, -41, 24, -125, -49, 2, 112, 22, 22, -63, 69]
    }
}
  • Python中的AES代码
# pip install pycryptodome
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

KEY = "fd6b639dbcff0c2a1b03b389ec763c4b"
IV = "77b07a672d57d64c"

def aes_encrypt(data_string):
    aes = AES.new(
        key=KEY.encode("utf-8"),
        mode=AES.MODE_CBC,
        iv=IV.encode("utf-8")
    )
    raw = pad(data_string.encode("utf-8"), 16)
    return aes.encrypt(raw)

data = aes_encrypt("一个小黑")
print(data) # b'xa5x82dIxa9x9fxd7x18x83xcfx02px16x16xc1E'
print([i for i in data])    # [165, 130, 100, 73, 169, 159, 215, 24, 131, 207, 2, 112, 22, 22, 193, 69]

注意事项

  • 遇到AES找key,找iv
  • 又时还会对加密后的密文进行base64编码

gzip压缩

  • Java中的gzip代码
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class Hello {
    public static void main(String[] args) throws IOException {
        // 压缩
        String data = "一个小黑";
        // System.out.println(Arrays.toString(data.getBytes()));
        ByteArrayOutputStream v0_1 = new ByteArrayOutputStream();
        GZIPOutputStream v1 = new GZIPOutputStream((v0_1));
        v1.write(data.getBytes());
        v1.close();
        
        byte[] arg6 = v0_1.toByteArray();  //gzip压缩后:arg6
        System.out.println(Arrays.toString(arg6));	// [31, -117, 8, 0, 0, 0, 0, 0, 0, -1, 123, -78, -93, -31, -55, -114, 85, 79, 55, -12, -65, -36, 61, 17, 0, 44, -30, 103, 119, 12, 0, 0, 0]

        // 解压缩
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(arg6);
        GZIPInputStream ungzip = new GZIPInputStream(in);
        byte[] buffer = new byte[256];
        int n;
        while ((n = ungzip.read(buffer)) >= 0) {
            out.write(buffer, 0, n);
        }
        // byte[] res = out.toByteArray();
        // System.out.println(Arrays.toString(res));
        System.out.println(out.toString("UTF-8"));	// 一个小黑
    }
}
  • Python中的gzip代码
import gzip

s_in = "一个小黑".encode('utf-8')
s_out = gzip.compress(s_in)
print([i for i in s_out])   # [31, 139, 8, 0, 230, 159, 128, 98, 2, 255, 123, 178, 163, 225, 201, 142, 85, 79, 55, 244, 191, 220, 61, 17, 0, 44, 226, 103, 119, 12, 0, 0, 0]

res = gzip.decompress(s_out)
# print(res)
print(res.decode('utf-8'))  # 一个小黑

注意事项

  • gzip压缩一般用于设备指纹信息压缩
  • java和python的gzip压缩出来的结果,个别字节不一样,需要特殊处理

base64

  • Java中的base64代码
import java.util.Base64;

public class Hello {
    public static void main(String[] args) {
        String name = "一个小黑";
        // 加密
        Base64.Encoder encoder  = Base64.getEncoder();
        String res = encoder.encodeToString(name.getBytes());
        System.out.println(res); // 5LiA5Liq5bCP6buR
		
        // 解密
        Base64.Decoder decoder  = Base64.getDecoder();
        byte[] origin = decoder.decode(res);
        String data = new String(origin);
        System.out.println(data); // 一个小黑
    }
}
  • Python中的base64代码
import base64

name = "一个小黑"

res = base64.b64encode(name.encode('utf-8'))
print(res) # b'5LiA5Liq5bCP6buR'


data = base64.b64decode(res)
origin = data.decode('utf-8')
print(origin) # 一个小黑

最后

以上就是狂野小熊猫为你收集整理的逆向爬虫35 常见加密的python实现逆向爬虫35 常见加密的python实现的全部内容,希望文章能够帮你解决逆向爬虫35 常见加密的python实现逆向爬虫35 常见加密的python实现所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(58)

评论列表共有 0 条评论

立即
投稿
返回
顶部