概述
逆向爬虫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作为之后手机请求服务器的凭证
-
思路:
- 直接用固定uuid是否可行?
- 动态生成是否可行?
- 过程模拟,模拟获取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的的明文
-
读取请求头/请求体/url 数据,某个字段
-
读取数据拼接字符串
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实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复