逆向工程中的常见情况与处理方式

在逆向工程的实践中,我们经常会遇到各种各样的保护机制和技术难点。本文总结了一些常见的情况及其处理方式,希望对逆向分析工作有所帮助。

1. 隐藏字节 (Hidden Bytes)

1.1问题描述

程序在运行时动态生成或解密关键代码,静态分析时无法直接看到真实的执行逻辑。

1.2常见场景

  • 加密的字符串在运行时解密
  • 关键函数地址通过计算得出
  • 重要代码段被加密存储

1.3隐藏字节实例

在实际逆向中,经常遇到这样的情况:

1
2
3
// Java代码中的隐藏字符串
String salt = "xxssasdfasdfadsf";
String v4 = new String(new byte[]{-26, -83, -90, -26, -78, -101, -23, -67, -112});

这些负数实际上是有符号字节,需要转换才能得到真实内容:

1
2
3
4
5
6
7
8
9
10
11
# Python脚本,方便你们以后使用。
byte_list = [-26, -83, -90, -26, -78, -101, -23, -67, -112]

bs = bytearray() # python字节数组
for item in byte_list:
if item < 0:
item = item + 256 # 负数转换为无符号字节
bs.append(item)

str_data = bs.decode('utf-8') # data = bytes(bs)
print(str_data) # 输出: 海

解释:

  • Java中的byte是有符号的(-128到127)
  • 当字节值大于127时,会显示为负数
  • 通过加256转换为无符号字节(0-255)
  • 最后按UTF-8解码得到真实字符串

这种技术常用于隐藏敏感字符串,避免被静态分析工具直接发现。

重要提醒: 在逆向分析中,特别注意寻找这些常见的隐藏字符串:

  • MD5加密盐
  • AES加密key
  • iv向量

这些关键信息往往以字节数组形式隐藏,是破解加密算法的重要线索。

1.4反向操作:生成隐藏字节

如果你需要生成隐藏字节数组,可以用反向操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 将中文字符串转换为字节数组
data = "张三"
data_bytes = data.encode('utf-8')

data_list = bytearray()
for item in data_bytes:
data_list.append(item)

print(data_list)

# 解码验证
res = data_list.decode('utf-8')
print(res) # 输出: 张三

更简洁的写法:

1
2
3
4
5
6
7
8
9
# 使用普通列表收集字节
data = "张三"
data_bytes = data.encode('utf-8')

data_list = []
for item in data_bytes:
data_list.append(item)

print(data_list) # 输出字节值列表,可直接用于Java

这样就能得到对应的字节数组,在Java中可以直接使用这些字节值来隐藏字符串。

1.5处理方式

1
2
3
4
5
6
7
# 使用动态调试获取运行时数据
# 在关键位置设置断点
bp 0x401000
# 查看内存内容
x/100x $rsp
# 导出解密后的数据
dump memory decrypted.bin 0x401000 0x401100

工具推荐:

  • IDA Pro + 动态调试
  • x64dbg/OllyDbg
  • Frida 动态插桩

2. UUID

UUID(Universally Unique Identifier)是理论上永远不会重复的值,常用于生成唯一标识符(网卡、mac、当前时间等)。

常见场景:

  • 设备唯一标识
  • 会话ID生成
  • 防重放攻击的随机数

在逆向中的处理:

1
2
3
4
5
// Android中常见的UUID使用
import java.util.UUID;

// 生成随机UUID
String uuid = UUID.randomUUID().toString();
1
2
3
4
5
6
# Python中生成和处理UUID
import uuid

# 生成UUID4(随机)
uid = str(uuid.uuid4())
print(uid) # 输出类似: 550e8400-e29b-41d4-a716-446655440000

分析要点:

  • UUID通常用于生成设备指纹或会话标识
  • 在逆向分析中,关注UUID的生成逻辑和使用场景
  • 某些应用会基于设备信息生成固定的UUID作为设备ID

3. 随机值

随机值在逆向工程中经常遇到,特别是在加密、签名验证和防重放攻击中。

常见场景:

  • 加密算法的随机种子
  • 签名验证的随机数
  • 防重放攻击的nonce值
  • 设备指纹生成

Java中的随机值生成:

1
2
3
4
5
6
7
8
9
10
11
12
import java.math.BigInteger;
import java.security.SecureRandom;

public class Test {
public static void main(String[] args) {
// 随机生成80位,10个字节
BigInteger v4 = new BigInteger(80, new SecureRandom());
// 让字节以16进制展示
String res = v4.toString(16);
System.out.println(res);
}
}

Python中的随机值处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import random

# 方法1:生成随机字节并转换为16进制字符串
open_udid = "".join([hex(i)[2:] for i in random.randbytes(10)])
print(open_udid)

# 方法2:使用格式化字符串
open_udid = "".join(["%x" % i for i in random.randbytes(10)])
print(open_udid)

# 方法3:分步处理
data = random.randbytes(10)
ele_list = []
for item in data:
ele = hex(item)[2:] # 去掉0x前缀
ele_list.append(ele)

res = "".join(ele_list)
print(res)

# 方法4:补零格式化(推荐)
# 十六进制,不满2位数,前面加0
open_udid = "".join(["%02x" % i for i in random.randbytes(10)])
print(open_udid)

分析要点:

  • 关注随机数生成器的类型(伪随机vs真随机)
  • 分析随机种子的来源和可预测性
  • 在Android逆向中,注意SecureRandom的使用
  • 某些应用可能使用固定种子生成”随机”值

4. 时间戳

时间戳在逆向工程中经常作为唯一标识符、随机种子或防重放攻击的关键参数。

常见场景:

  • API请求的时间戳验证
  • 加密算法的时间种子
  • 日志记录和调试信息
  • 防重放攻击机制

Java中的时间戳处理:

1
2
3
4
5
6
7
8
9
10
11
public class Test {
public static void main(String[] args) {
// 获取当前时间戳(秒级)
String t1 = String.valueOf(System.currentTimeMillis() / 1000);
// 获取当前时间戳(毫秒级)
String t2 = String.valueOf(System.currentTimeMillis());

System.out.println(t1); // 秒级时间戳
System.out.println(t2); // 毫秒级时间戳
}
}

Python中的时间戳处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time
from datetime import datetime

# 方法1:获取当前时间戳
timestamp_sec = int(time.time()) # 秒级时间戳
timestamp_ms = int(time.time() * 1000) # 毫秒级时间戳

print(f"秒级时间戳: {timestamp_sec}")
print(f"毫秒级时间戳: {timestamp_ms}")

# 方法2:使用datetime
dt = datetime.now()
timestamp_sec2 = int(dt.timestamp())
timestamp_ms2 = int(dt.timestamp() * 1000)

print(f"datetime秒级: {timestamp_sec2}")
print(f"datetime毫秒级: {timestamp_ms2}")

# 时间戳转换为可读格式
readable_time = datetime.fromtimestamp(timestamp_sec)
print(f"可读时间: {readable_time}")

# 字符串格式化
time_str = datetime.fromtimestamp(timestamp_sec).strftime("%Y-%m-%d %H:%M:%S")
print(f"格式化时间: {time_str}")

分析要点:

  • 时间戳常用于生成请求签名和验证
  • 注意时间戳的精度(秒级vs毫秒级)
  • 某些应用会对时间戳进行偏移或加密处理
  • 在逆向分析中,时间戳可能是突破口之一

5. 16进制字符串

16进制字符串在逆向工程中广泛使用,特别是在处理二进制数据、加密密钥和哈希值时。

常见场景:

  • 加密密钥的表示
  • 哈希值的显示
  • 二进制数据的可读化
  • 网络协议中的数据传输

Java中的16进制处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static String H(byte[] arg5) {
StringBuilder v0 = new StringBuilder();
int v1 = arg5.length;
int v2;
for(v2 = 0; v2 < v1; ++v2) {
int v3 = arg5[v2] & 0xFF; // 转换为无符号字节
if(v3 < 16) {
v0.append('0'); // 补零
}
v0.append(Integer.toHexString(v3));
}
return v0.toString();
}

Python中的16进制处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 在Java中字节是有符号:-128 ~ 127

# name_bytes = "张三".encode('utf-8')
name_bytes = [10, -26, -83, -90, -26, -78, -101, -23, -67, -112]

data_list = []

for item in name_bytes:
item = item & 0xff # item<0时,让item+256
ele = "%02x" % item # 格式化为2位16进制,不足补0
data_list.append(ele)

print("".join(data_list))

更简洁的Python实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 方法1:直接处理字节数组
def bytes_to_hex(byte_array):
return "".join(["%02x" % (item & 0xff) for item in byte_array])

# 方法2:使用内置方法
def bytes_to_hex_builtin(data):
if isinstance(data, str):
data = data.encode('utf-8')
return data.hex()

# 方法3:处理有符号字节
def signed_bytes_to_hex(signed_bytes):
return "".join(["%02x" % (b & 0xff) for b in signed_bytes])

# 示例使用
name_bytes = [10, -26, -83, -90, -26, -78, -101, -23, -67, -112]
hex_string = signed_bytes_to_hex(name_bytes)
print(hex_string) # 输出16进制字符串

分析要点:

  • 注意Java中字节的有符号特性(-128到127)
  • 使用& 0xFF操作将有符号字节转换为无符号
  • %02x格式化确保每个字节都是2位16进制
  • 在逆向分析中,16进制字符串常用于表示密钥、哈希等关键数据

6. MD5加密

MD5在逆向工程中是最常见的哈希算法之一,广泛用于数据完整性验证、密码存储和签名生成。

常见场景:

  • API请求签名验证
  • 密码哈希存储
  • 文件完整性校验
  • 防篡改机制

Python中的MD5实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
import hashlib

# 基础MD5加密
obj = hashlib.md5()
obj.update('xxxxx'.encode('utf-8'))

# 获取16进制字符串(Java中没有这个功能)
v1 = obj.hexdigest()
print(v1) # fb0e22c79ac75679e9881e6ba183b354

# 获取字节数组
v2 = obj.digest()
print(v2) # b'\xfb\x0e"\xc7\x9a\xc7Vy\xe9\x88\x1ek\xa1\x83\xb3T'

更完整的Python实现:

1
2
3
4
5
6
7
8
9
10
11
import hashlib

# 对中文字符串进行MD5加密
m = hashlib.md5()
m.update("张三".encode("utf-8"))

v1 = m.digest()
print(v1) # b'\x175\x10\x12GrB\x9d5-\x0c\r#\xd4h\x17='

v2 = m.hexdigest()
print(v2) # 1735101247242d52d0c0d23d468173d

Java中的MD5实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.security.MessageDigest;

public static void main(String[] args) throws NoSuchAlgorithmException {
String name = "张三";

MessageDigest instance = MessageDigest.getInstance("MD5");
byte[] nameBytes = instance.digest(name.getBytes());

// 十六进制展示
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); // e6ada6e6b29be9bd90
}

MD5加盐处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import hashlib

def md5_with_salt(data, salt):
"""MD5加盐加密"""
# 方法1:数据+盐
combined = data + salt
return hashlib.md5(combined.encode('utf-8')).hexdigest()

def md5_double_salt(data, salt1, salt2):
"""双重加盐"""
combined = salt1 + data + salt2
return hashlib.md5(combined.encode('utf-8')).hexdigest()

# 示例使用
password = "123456"
salt = "mysalt"
hashed = md5_with_salt(password, salt)
print(f"加盐MD5: {hashed}")

实际应用场景 - 抖音X-SS-STUB:

在抖音等应用的逆向分析中,经常遇到这样的场景:

1
2
3
4
每次发送POST请求时,抖音都会携带一些请求头:
X-SS-STUB = "fjaku9asdf"

读取请求体中的数据,对请求体中的数据进行md5加密。

这种机制用于防止请求被篡改,是典型的API签名验证方式。

分析要点:

  • MD5虽然已被认为不够安全,但在逆向分析中仍然常见
  • 注意加盐的方式和位置(前缀、后缀、中间插入)
  • Java中需要手动转换字节为16进制字符串
  • Python的hexdigest()方法直接返回16进制字符串
  • 在API逆向中,MD5常用于生成请求签名

7. SHA-256加密

SHA-256是SHA-2系列中最常用的哈希算法,比MD5更安全,在现代应用中广泛使用。

常见场景:

  • 区块链和加密货币
  • 数字签名验证
  • 密码安全存储
  • API签名生成

Java中的SHA-256实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Hello {
public static void main(String[] args) throws NoSuchAlgorithmException {
String name = "张三";
MessageDigest instance = MessageDigest.getInstance("SHA-256");
byte[] nameBytes = instance.digest(name.getBytes());

// 十六进制展示
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);
}
}

Python中的SHA-256实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
import hashlib

# 基础SHA-256加密
m = hashlib.sha256()
m.update("张三".encode("utf-8"))

# 获取字节数组
v1 = m.digest()
print(v1)

# 获取16进制字符串
v2 = m.hexdigest()
print(v2)

分析要点:

  • SHA-256输出长度固定为64个16进制字符(256位)
  • 比MD5更安全,抗碰撞能力更强
  • 在区块链和现代加密应用中广泛使用
  • Java和Python的实现方式类似,但Java需要手动处理字节转换

8. AES加密

AES(Advanced Encryption Standard)是目前最广泛使用的对称加密算法,在逆向工程中经常遇到。

常见场景:

  • 敏感数据加密存储
  • 网络通信加密
  • 配置文件保护
  • API数据传输加密

Python中的AES实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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)
print([i for i in data]) # 显示字节数组

Java中的AES实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;

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));
}
}

AES解密实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

def aes_decrypt(encrypted_data, key, iv):
"""AES解密函数"""
aes = AES.new(
key=key.encode('utf-8'),
mode=AES.MODE_CBC,
iv=iv.encode('utf-8')
)
decrypted = aes.decrypt(encrypted_data)
return unpad(decrypted, 16).decode('utf-8')

# 解密示例
KEY = "fd6b639dbcff0c2a1b03b389ec763c4b"
IV = "77b07a672d57d64c"
encrypted = aes_encrypt("张三")
decrypted = aes_decrypt(encrypted, KEY, IV)
print(f"解密结果: {decrypted}")

常见AES模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os

def aes_ecb_encrypt(data, key):
"""ECB模式加密"""
aes = AES.new(key.encode('utf-8'), AES.MODE_ECB)
padded_data = pad(data.encode('utf-8'), 16)
return aes.encrypt(padded_data)

def aes_cbc_encrypt(data, key, iv):
"""CBC模式加密"""
aes = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
padded_data = pad(data.encode('utf-8'), 16)
return aes.encrypt(padded_data)

def aes_ctr_encrypt(data, key):
"""CTR模式加密"""
nonce = os.urandom(8) # 8字节随机数
aes = AES.new(key.encode('utf-8'), AES.MODE_CTR, nonce=nonce)
return aes.encrypt(data.encode('utf-8'))

在逆向分析中的要点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 常见的密钥和IV获取方式
def analyze_aes_usage():
"""分析AES使用模式"""
patterns = {
"硬编码密钥": "直接在代码中写死的密钥",
"动态生成": "基于设备信息或时间戳生成",
"网络获取": "从服务器动态获取密钥",
"密钥派生": "通过PBKDF2等算法派生"
}

modes = {
"ECB": "最简单但不安全,相同明文产生相同密文",
"CBC": "需要IV,最常用的模式",
"CTR": "流加密模式,需要nonce",
"GCM": "认证加密,提供完整性保护"
}

return patterns, modes

分析要点:

  • AES是对称加密,加密和解密使用相同密钥
  • 密钥长度通常为128、192或256位
  • CBC模式需要IV(初始化向量),ECB模式不需要
  • 注意填充方式:PKCS5Padding、PKCS7Padding等
  • 在逆向中重点关注密钥和IV的来源
  • Java中需要指定完整的算法描述:”AES/CBC/PKCS5Padding”

9. GZIP压缩

GZIP是一种广泛使用的数据压缩算法,在逆向工程中经常遇到,特别是在网络传输和数据存储中。

常见场景:

  • HTTP响应数据压缩
  • 日志文件压缩存储
  • 配置文件压缩
  • 网络传输数据减少

Java中的GZIP实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.io.*;
import java.util.zip.*;

public class Hello {
public static void main(String[] args) throws IOException {
// 压缩
String data = "张三";

ByteArrayOutputStream v0_1 = new ByteArrayOutputStream();
GZIPOutputStream v1 = new GZIPOutputStream(v0_1);
v1.write(data.getBytes());
v1.close();

byte[] arg6 = v0_1.toByteArray(); // gzip压缩后: arg6

// 解压缩
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(out.toString("UTF-8"));
}
}

Python中的GZIP实现:

1
2
3
4
5
6
7
8
9
10
11
import gzip

# 压缩
s_in = "我是张三".encode('utf-8')
s_out = gzip.compress(s_in)
print([i for i in s_out])

# 解压缩
res = gzip.decompress(s_out)
print(res)
print(res.decode('utf-8'))

更完整的Python实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import gzip
import io

def gzip_compress(data):
"""GZIP压缩函数"""
if isinstance(data, str):
data = data.encode('utf-8')
return gzip.compress(data)

def gzip_decompress(compressed_data):
"""GZIP解压缩函数"""
return gzip.decompress(compressed_data)

def gzip_compress_to_file(data, filename):
"""压缩数据到文件"""
with gzip.open(filename, 'wb') as f:
if isinstance(data, str):
data = data.encode('utf-8')
f.write(data)

def gzip_decompress_from_file(filename):
"""从文件解压缩数据"""
with gzip.open(filename, 'rb') as f:
return f.read()

# 示例使用
original_data = "我是张三"
compressed = gzip_compress(original_data)
decompressed = gzip_decompress(compressed)

print(f"原始数据: {original_data}")
print(f"压缩后字节: {[i for i in compressed]}")
print(f"解压缩结果: {decompressed.decode('utf-8')}")

在逆向分析中的应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import gzip
import base64

def analyze_compressed_data(data):
"""分析可能的压缩数据"""
# 检查GZIP魔数
if data.startswith(b'\x1f\x8b'):
print("检测到GZIP压缩数据")
try:
decompressed = gzip.decompress(data)
print(f"解压缩成功,原始大小: {len(decompressed)}")
return decompressed
except Exception as e:
print(f"解压缩失败: {e}")

# 检查Base64编码的GZIP数据
try:
decoded = base64.b64decode(data)
if decoded.startswith(b'\x1f\x8b'):
print("检测到Base64编码的GZIP数据")
decompressed = gzip.decompress(decoded)
return decompressed
except:
pass

return None

# HTTP响应处理示例
def handle_http_response(response_data, headers):
"""处理HTTP响应中的GZIP数据"""
if headers.get('Content-Encoding') == 'gzip':
try:
decompressed = gzip.decompress(response_data)
return decompressed.decode('utf-8')
except Exception as e:
print(f"GZIP解压缩失败: {e}")
return response_data

压缩比分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import gzip

def compression_analysis(data):
"""分析压缩效果"""
if isinstance(data, str):
original_bytes = data.encode('utf-8')
else:
original_bytes = data

compressed = gzip.compress(original_bytes)

original_size = len(original_bytes)
compressed_size = len(compressed)
ratio = (1 - compressed_size / original_size) * 100

print(f"原始大小: {original_size} 字节")
print(f"压缩后大小: {compressed_size} 字节")
print(f"压缩比: {ratio:.2f}%")

return compressed

# 示例分析
test_data = "我是张三" * 100 # 重复数据压缩效果更好
compression_analysis(test_data)

重要提醒: Java、Python语言区别。(个人字节是不同,不影响整个的结果)

分析要点:

  • GZIP文件以魔数1F 8B开头,这是识别GZIP数据的重要特征
  • 压缩比取决于数据的重复性和模式
  • 在网络抓包分析中,注意Content-Encoding: gzip头部
  • Java实现相对复杂,需要使用流操作
  • Python的gzip模块提供了简单的压缩/解压缩接口
  • 在逆向分析中,GZIP常用于隐藏或减少数据传输量

10. Base64编码

Base64是一种基于64个可打印字符来表示二进制数据的编码方法,在逆向工程中极其常见。

常见场景:

  • 数据传输编码
  • 配置文件中的二进制数据
  • 网络协议中的数据编码
  • 图片和文件的文本表示

Python中的Base64实现:

1
2
3
4
5
6
7
8
9
10
11
12
import base64

name = "张三"

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

# 解码
data = base64.b64decode(res)
origin = data.decode('utf-8')
print(origin) # "张三"

Java中的Base64实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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); // "5q2m5rKb6b2Q"

// 解密
Base64.Decoder decoder = Base64.getDecoder();
byte[] origin = decoder.decode(res);
String data = new String(origin);
System.out.println(data); // 张三
}
}

更完整的Python实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import base64

def base64_encode(data):
"""Base64编码函数"""
if isinstance(data, str):
data = data.encode('utf-8')
return base64.b64encode(data).decode('ascii')

def base64_decode(encoded_data):
"""Base64解码函数"""
if isinstance(encoded_data, str):
encoded_data = encoded_data.encode('ascii')
return base64.b64decode(encoded_data)

def base64_url_encode(data):
"""URL安全的Base64编码"""
if isinstance(data, str):
data = data.encode('utf-8')
return base64.urlsafe_b64encode(data).decode('ascii')

def base64_url_decode(encoded_data):
"""URL安全的Base64解码"""
if isinstance(encoded_data, str):
encoded_data = encoded_data.encode('ascii')
return base64.urlsafe_b64decode(encoded_data)

# 示例使用
text = "张三"
encoded = base64_encode(text)
decoded = base64_decode(encoded)

print(f"原文: {text}")
print(f"编码: {encoded}")
print(f"解码: {decoded.decode('utf-8')}")

在逆向分析中的应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import base64
import re

def detect_base64(data):
"""检测可能的Base64编码数据"""
# Base64字符集检查
base64_pattern = re.compile(r'^[A-Za-z0-9+/]*={0,2}$')

if isinstance(data, bytes):
data = data.decode('ascii', errors='ignore')

# 长度检查(Base64编码后长度是4的倍数)
if len(data) % 4 != 0:
return False

# 字符集检查
if not base64_pattern.match(data):
return False

try:
decoded = base64.b64decode(data)
# 检查解码后是否为可打印字符
if all(32 <= b <= 126 or b in [9, 10, 13] for b in decoded):
return True
except:
pass

return False

def analyze_base64_data(data):
"""分析Base64数据"""
if detect_base64(data):
try:
decoded = base64.b64decode(data)
print(f"检测到Base64数据")
print(f"原始长度: {len(data)}")
print(f"解码后长度: {len(decoded)}")
print(f"解码内容: {decoded}")

# 尝试UTF-8解码
try:
text = decoded.decode('utf-8')
print(f"UTF-8文本: {text}")
except:
print("非UTF-8文本数据")

return decoded
except Exception as e:
print(f"Base64解码失败: {e}")
else:
print("不是有效的Base64数据")

return None

# 多层编码检测
def detect_nested_encoding(data):
"""检测多层Base64编码"""
current = data
layers = 0

while detect_base64(current) and layers < 10: # 防止无限循环
try:
decoded = base64.b64decode(current)
current = decoded.decode('ascii', errors='ignore')
layers += 1
print(f"第{layers}层Base64解码: {current}")
except:
break

return layers, current

Base64变种处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import base64
import string

def base64_custom_alphabet(data, alphabet):
"""自定义字母表的Base64编码"""
standard = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

# 创建转换表
trans_table = str.maketrans(alphabet, standard)

# 转换为标准Base64然后解码
standard_b64 = data.translate(trans_table)
return base64.b64decode(standard_b64)

def analyze_custom_base64(data):
"""分析可能的自定义Base64编码"""
# 常见的自定义字母表
custom_alphabets = [
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", # URL safe
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/", # 数字开头
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/", # 小写开头
]

for i, alphabet in enumerate(custom_alphabets):
try:
decoded = base64_custom_alphabet(data, alphabet)
print(f"自定义字母表{i+1}解码成功: {decoded}")
except:
continue

分析要点:

  • Base64编码后的字符串长度总是4的倍数
  • 只包含A-Z、a-z、0-9、+、/和=(填充)字符
  • URL安全版本使用-和_替代+和/
  • 在逆向分析中,Base64常用于隐藏配置信息、密钥等
  • 注意多层Base64编码的情况
  • 某些应用会使用自定义的Base64字母表

11. 反调试技术

常见反调试手段

  • IsDebuggerPresent() 检测
  • PEB 结构检查
  • 时间差检测
  • 异常处理反调试

处理方式

11.1 API Hook 绕过

1
2
3
4
// Hook IsDebuggerPresent
BOOL WINAPI FakeIsDebuggerPresent() {
return FALSE;
}

11.2 内存补丁

1
2
3
4
; 将检测代码 nop 掉
mov eax, 0 ; 原: call IsDebuggerPresent
nop ; 原: test eax, eax
nop ; 原: jnz exit

11.3 PEB 修改

1
2
3
4
# 使用 Frida 修改 PEB
def modify_peb():
peb_addr = Process.getCurrentProcess().getPointer("PEB")
peb_addr.add(0x02).writeU8(0) # BeingDebugged = 0

12. 代码混淆

常见混淆技术

  • 控制流平坦化
  • 虚假控制流
  • 指令替换
  • 垃圾代码插入

处理策略

12.1 控制流分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 使用 IDA Python 分析控制流
import idaapi
import idc

def analyze_cfg(start_addr):
"""分析控制流图"""
visited = set()
queue = [start_addr]

while queue:
addr = queue.pop(0)
if addr in visited:
continue
visited.add(addr)

# 获取指令
insn = idaapi.insn_t()
idaapi.decode_insn(insn, addr)

# 分析跳转目标
for i in range(insn.Op1.type, insn.Op6.type):
if insn.ops[i].type == idaapi.o_near:
queue.append(insn.ops[i].addr)

12.2 去混淆脚本

1
2
3
4
5
6
7
8
9
10
11
# 移除垃圾指令的简单示例
def remove_junk_code(start, end):
addr = start
while addr < end:
insn = idc.GetDisasm(addr)
# 识别垃圾指令模式
if "push eax" in insn and "pop eax" in idc.GetDisasm(addr + idc.ItemSize(addr)):
# 将垃圾指令 nop 掉
idc.PatchByte(addr, 0x90)
idc.PatchByte(addr + 1, 0x90)
addr = idc.NextHead(addr)

13. 字符串加密

识别方法

  • 查找解密函数特征
  • 分析字符串使用模式
  • 动态跟踪字符串生成

解密策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 自动化字符串解密
def decrypt_strings():
# 找到解密函数
decrypt_func = 0x401234

# 遍历所有调用
for xref in idautils.XrefsTo(decrypt_func):
# 获取参数
encrypted_str = idc.GetOperandValue(xref.frm - 5, 0)
key = idc.GetOperandValue(xref.frm - 10, 0)

# 解密并添加注释
decrypted = simple_xor_decrypt(encrypted_str, key)
idc.MakeComm(xref.frm, f"Decrypted: {decrypted}")

14. 虚拟机保护

特征识别

  • 大量的间接跳转
  • 复杂的解释器循环
  • 自定义指令集

分析方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# VM 指令跟踪
def trace_vm_instructions():
vm_context = get_vm_context()
instruction_log = []

while vm_context.running:
opcode = vm_context.fetch_opcode()
instruction_log.append({
'pc': vm_context.pc,
'opcode': opcode,
'operands': vm_context.get_operands()
})
vm_context.execute()

return instruction_log

15. 动态分析技巧

15.1 内存断点

1
2
3
4
# GDB 中设置内存访问断点
watch *0x401000
awatch *0x401000 # 访问断点
rwatch *0x401000 # 读取断点

15.2 API 监控

1
2
3
4
5
6
7
8
9
10
// Frida 脚本监控 API 调用
Java.perform(function() {
var targetClass = Java.use("com.example.TargetClass");
targetClass.sensitiveMethod.implementation = function(param) {
console.log("[+] sensitiveMethod called with: " + param);
var result = this.sensitiveMethod(param);
console.log("[+] Result: " + result);
return result;
};
});

16. 静态分析优化

16.1 函数识别

1
2
3
4
5
6
7
8
9
10
11
# 自动识别加密函数
def find_crypto_functions():
crypto_patterns = [
b'\x31\xc0\x99\xf7\xf1', # xor eax,eax; cdq; div ecx
b'\xd1\xe8\x73\x02', # shr eax,1; jnc +2
]

for pattern in crypto_patterns:
addr = idc.FindBinary(0, idc.SEARCH_DOWN, pattern)
if addr != idc.BADADDR:
print(f"Potential crypto function at: {hex(addr)}")

16.2 交叉引用分析

1
2
3
4
5
6
7
8
9
10
11
# 分析关键数据的使用
def analyze_key_usage(key_addr):
xrefs = []
for xref in idautils.XrefsTo(key_addr):
func_name = idc.GetFunctionName(xref.frm)
xrefs.append({
'addr': xref.frm,
'function': func_name,
'type': xref.type
})
return xrefs

17. 实用工具与脚本

17.1 自动化分析脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python3
# 逆向分析自动化脚本

import subprocess
import os

def auto_analysis(binary_path):
"""自动化逆向分析流程"""

# 1. 基础信息收集
print("[+] 收集基础信息...")
os.system(f"file {binary_path}")
os.system(f"strings {binary_path} | head -20")

# 2. 反汇编
print("[+] 生成反汇编...")
os.system(f"objdump -d {binary_path} > disasm.txt")

# 3. 符号表分析
print("[+] 分析符号表...")
os.system(f"nm {binary_path} > symbols.txt")

# 4. 动态库依赖
print("[+] 检查依赖...")
os.system(f"ldd {binary_path}")

if __name__ == "__main__":
auto_analysis("target_binary")

17.2 内存 Dump 工具

1
2
3
4
5
6
7
8
9
10
11
12
13
def dump_process_memory(pid, start_addr, size):
"""导出进程内存"""
try:
with open(f"/proc/{pid}/mem", "rb") as mem_file:
mem_file.seek(start_addr)
data = mem_file.read(size)

with open(f"dump_{hex(start_addr)}.bin", "wb") as dump_file:
dump_file.write(data)

print(f"[+] 内存已导出到 dump_{hex(start_addr)}.bin")
except Exception as e:
print(f"[-] 导出失败: {e}")
支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者,更多功能请访问博客站



逆向工程中的常见情况与处理方式
https://blog.fxcxy.com/2025/05/28/逆向工程中的常见情况与处理方式/
作者
spatacus
发布于
2025年5月28日
许可协议