☰
base64和密码算法常量特征识别
2026/10/19 2:10:08 网站建设 项目流程

base64

1.编码原理

将每3个8位的字节(共24位)重新分组为4个6位的单元

然后将每个6位单元映射到一个特定的可打印字符。

过程:

将每个字符转换为ASC码的二进制值

按6位一组进行分割

用索引表进行映射

如果原始数据的字节数不是3的倍数,就需要进行填充,使用 = 字符

数据字节数 % 3 == 0 -> 无填充

数据字节数 % 3 == 1 -> 补足2字节,编码结果末尾加 ==

数据字节数 % 3 == 2 -> 补足1字节,编码结果末尾加 =

索引表

索引字符索引字符索引字符索引字符
0A16Q32g48w
1B17R33h49x
50y18S34i51z
3D19T35j520
4E20U36k531
5F21V37l542
6G22W38m553
7H23X39n564
8I24Y40o575
9J25Z41p586
10K26a42q597
11L27b43r608
12M28c44s619
13N29d45t62+
14O30e46u63/
15P31f47v

2.常见变形

修改编码表 :

将 Base64 的编码表替换成另外一种编码表->将表的数据替换成原来的表即可实现解密

修改下标:

将 Base64 的编码查表下标对应关系修改->推导出下标逆计算即可

实现方法变形:

将原始数据转成大整数,利用进制256->64计算base64编码

3.逆向方法:

常量表:提供了Base64编码的索引表

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

在代码中的体现:

.rdata:00503E4F db 0 .rdata:00503E50 aAbcdefghijklmn db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', ​ .rdata:00503E50 ; DATA XREF: .data:off_529000↓o ​ .rdata:00503F91 align 10h

查表(含有0x3F):核心的3字节到4字节的转换与编码过程

在代码中的体现:

4*(_BYTE *)(v8 + a2) = '='; *(_BYTE *)(++v8 + a2) = '='; v6 = v8 + 1; *(_BYTE *)(v9 + a2) = off_529000[v5]; v10 = v9 + 1; *(_BYTE *)(v10 + a2) = '=';

Padding计算:处理输入数据不是3字节倍数时的末尾填充

用 = 填充末尾,标记无效数据,应对非3倍数输入

在代码中的体现:

*(v6 + a2) = off_529000[(a1[v12] >> 2) & 0x3F];*(v7 + a2) = off_529000[(a1[v12 + 1] >> 4) & 0xF | v4];*(v9 + a2) = off_529000[(a1[v12 + 2] >> 6) & 3 | v5];

改表:

找index下标:从密文字符反推出编码时使用的索引值

应对方法:

建立索引映射关系 分析自定义表,数密文每个字母对应index多少

将索引转为6位二进制

将4个6位重组为3个8位字节

密码算法常量特征识别

1.什么是特征常量 :

在逆向过程中,经常遇到一些开源的代码,如果能找到这些开源代码,可以减少许多逆向工作量

如何寻找某段代码的开源代码:使用特征常量作为关键字搜索。 特征常量是算法中用到的特征整数或字符串或字节数组等

常见特征常量: 1. 整数特征 2. 字符串特征 3. 字节数据特征 如果我们能找到这些开源代码的那个源码

2.常见加密算法的特征常量

*处理时把十进制和十六进制都试一遍

AES 算法特征常量(S盒):

db 63h, 7Ch, 77h, 7Bh, 0F2h, 6Bh, 6Fh, 0C5h, 30h, 1, 67h b ​ ; DATA XREF: sub_1400012A0+5E↑o ​ db 2Bh, 0FEh, 0D7h, 0ABh, 76h, 0CAh, 82h, 0C9h, 7Dh, 0FAh ​ db 59h, 47h, 0F0h, 0ADh, 0D4h, 0A2h, 0AFh, 9Ch, 0A4h, 72h ​ db 0C0h, 0B7h, 0FDh, 93h, 26h, 36h, 3Fh, 0F7h, 0CCh, 34h ​ db 0A5h, 0E5h, 0F1h, 71h, 0D8h, 31h, 15h, 4, 0C7h, 23h ​ db 0C3h, 18h, 96h, 5, 9Ah, 7, 12h, 80h, 0E2h, 0EBh, 27h ​ db 0B2h, 75h, 9, 83h, 2Ch, 1Ah, 1Bh, 6Eh, 5Ah, 0A0h, 52h ​ db 3Bh, 0D6h, 0B3h, 29h, 0E3h, 2Fh, 84h, 53h, 0D1h, 0 ​ db 0EDh, 20h, 0FCh, 0B1h, 5Bh, 6Ah, 0CBh, 0BEh, 39h, 4Ah ​ db 4Ch, 58h, 0CFh, 0D0h, 0EFh, 0AAh, 0FBh, 43h, 0Dh, 33h ​ db 85h, 45h, 0F9h, 2, 7Fh, 50h, 3Ch, 9Fh, 0A8h, 51h, 0A3h ​ db 40h, 8Fh, 92h, 9Dh, 38h, 0F5h, 0BCh, 0B6h, 0DAh, 21h ​ db 10h, 0FFh, 0F3h, 0D2h, 0CDh, 0Ch, 13h, 0ECh, 5Fh, 97h ​ db 44h, 17h, 0C4h, 0A7h, 7Eh, 3Dh, 64h, 5Dh, 19h, 73h ​ db 60h, 81h, 4Fh, 0DCh, 22h, 2Ah, 90h, 88h, 46h, 0ECh ​ db 0B8h, 14h, 0DEh, 5Eh, 0Bh, 0DBh, 0E0h, 32h, 3Ah, 0Ah ​ db 49h, 6, 24h, 5Ch, 0C2h, 0D3h, 0Ah, 62h, 91h, 95h, 0E4h ​ db 79h, 0E7h, 0C8h, 37h, 6Dh, 8Dh, 0D5h, 4Eh, 0A9h, 6Ch

//AES算法:

AES:高级加密标准

核心目标:将一段原始数据(明文),通过一系列复杂的数学变换,转换成乱码(密文)。没有密钥的人无法从密文还原出明文。

S盒

唯一任务:把一个字节的值,替换成另一个完全不同的值,是一个包含256个数字的固定表格。在加密过程的“字节替换”步骤中,AES算法会取出数据中的每一个字节,以这个字节的值作为索引,去S盒里查找对应的新值,然后用这个新值替换掉原来的字节。

工作原理(举例说明):

当前正在处理的一个字节的值是 0x53

  1. 查找:算法将这个值 0x53 作为索引,去S盒中查找第83项

  2. 替换:根据标准的AES S盒,索引 0x53 对应的值是 0xED

  3. 结果:原来的 0x53 就被替换成了 0xED

这个过程对加密过程中的每一个字节都会执行一次

在IDA看到连续的256个字节数据时:

  1. 这个程序使用了AES加密

  2. 需要去代码中寻找引用这个S盒数据的函数,那些函数很可能就是加密或解密函数

  3. 分析这些函数,找到加密时使用的密钥和加密的模式

MD5 算法初始常量

weak void md5Init(Md5Context *context) { //Set initial hash value context->h[0] = 0x67452301; context->h[1] = 0xEFCDAB89; context->h[2] = 0x98BADCFE; context->h[3] = 0x10325476; //Number of bytes in the buffer context->size = 0; //Total length of the message context->totalSize = 0; }

//MD5算法:

是一种广泛使用的密码散列函数,为任意长度的数据(如字符串、文件)生成固定长度的数字

MD5特点:

  1. 固定长度输出:计算出的结果永远是 128位通常表示为32个十六进制字符

  2. 确定性:同样的输入,无论在任何计算机上、计算多少次,得到的MD5值都完全相同

  3. 输入的微小改变会导致输出的MD5值发生巨大的改变

  4. 不可逆性:从输出的MD5哈希值不可能反向推导出原始的输入数据,是一个单向的函数

MD5的密码学弱点:

  1. 碰撞攻击

    不同的输入数据,产生了相同的MD5输出,从而绕过文件完整性检查

  2. 进行蛮力破解或查表,找到对应某个MD5值的原始密码

识别MD5:

在代码中看到一些魔数常量,如0x67452301, 是MD5算法初始化时使用的标准常数;

看到一个函数生成一个32位的十六进制字符串,很可能是MD5

解题思路:

如果程序将输入进行MD5计算,然后与一个固定的MD5值进行比较。

找到一个输入,使其MD5值等于目标值。

方法:查在线数据库;利用碰撞

CRC32 常量表

static const unsigned int crc32_table[] = { 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, ……

//CRC算法:用于检测数据错误的算法

作用:验证数据在传输或存储过程中是否发生了意外改变,数据被篡改,其 CRC32 值极大概率会发生改变

  1. 基本原理:CRC32 是逐字节计算,对于一个给定的字节,它与当前的 CRC 值进行计算后,会得到一个新的 CRC 值。

  2. 预计算:一个字节有 256 种可能(0x00 ~ 0xFF),可以预先为这 256 种可能的输入字节,计算出它们对应的 CRC32 结果

  3. 查表:

  4. 实际计算数据流的 CRC32 时只需要:

    取出数据的一个字节

    以这个字节为 索引,去常量表中查找对应的预计算值

    通过一个简单的操作(通常是异或)将这个预计算值与当前的 CRC 值结合

CRC32 计算函数使用查表法,代码应与下面相似;

uint32_t calculate_crc32(const uint8_t *data, size_t length) { ​ uint32_t crc = 0xFFFFFFFF; for (size_t i = 0; i < length; i++) { uint8_t table_index = (crc ^ data[i]) & 0xFF; crc = (crc >> 8) ^ crc32_table[table_index]; } return crc ^ 0xFFFFFFFF; }

表现:

  1. 初始化

  2. 遍历数据的每一个字节 。

  3. 将当前 CRC 值的低 8 位与数据字节进行异或,得到一个 0~255 的索引

  4. 将当前 CRC 值右移 8 位,抛弃最低的那个字节,然后与从常量表中查到的值进行异或

  5. 处理完所有数据后,对最终的 CRC 值取反后返回

识别CRC32:

  1. 一个 256 * 4 = 1024 字节的常量数据区:一个很大的数组,里面有 256 个DWORD数据。

  2. 特定的初始值和最终异或值,出现 0xFFFFFFFF 作为初始值或用于最终异或

  3. 特征循环结构:

    从数据中取一个字节

    AND 0xFF` 操作(计算索引)

    右移 8 位(SHR或>> 8)的操作

    从大数组中查表的操作

    异或(XOR)操作

除了加密算法之外,其他代码的特征常量也可以用于源码查找:

数据库系统:

sqlite3

魔数:数据库文件的前16个字节是:SQLite format 3\000

系统表sqlite_master

SQL语句:大量标准的SQL关键字

mysql/mariaDB客户端库

连接握手:数据包中包含特定的协议版本号

错误码:特定的错误码常量,如 CR_UNKNOWN_ERROR, CR_SERVER_GONE_ERROR

压缩库:

zlib / gzip / DEFLATE

压缩头:

zlib: 0x789C 或 0x78DA

gzip: 文件头以 0x1F 0x8B 开始

Adler-32 校验和:zlib使用的校验算法

LZMA / 7-Zip

魔数:压缩数据流通常以 0x5D 00 00 开头

可执行文件格式

PE 文件

DOS 头:MZ(0x4D 0x5A)

PE 签名:PE\0\0 (0x50 0x45 0x00 0x00)

ELF 文件

魔数:0x7F 'E' 'L' 'F'(0x7F 0x45 0x4C 0x46)

Mach-O 文件

魔数:0xFEEDFACE(32位) 或 0xFEEDFACF (64位)

多媒体格式

图片格式

JPEG: 0xFF 0xD8 0xFF

PNG: 0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A

GIF: GIF89a 或 GIF87a

音频/视频格式

MP3: 可能以 ID3 标签开始,或帧同步字 0xFF 0xFB

WAV: RIFF头和 WAVE 格式标识符

网络协议

HTTP

方法:GET, POST, PUT, DELETE 等

版本:HTTP/1.1, HTTP/2

头部字段:User-Agent:, Content-Type:, Cookie:等

TLS/SSL

握手协议:0x16 0x03 开始的记录层头。

ClientHello:包含 TLS 版本信息如 0x03 0x03 (TLS 1.2)

编程语言运行

Python

pyc 文件魔数:不同版本的Python有不同的魔数,如 Python 3.6 的是 0x42 0x0D 0x0D 0x0A

字符串:大量与Python内部相关的字符串,如 _main_, _name_, sys, import, print

Java (JVM)

Class 文件魔数:0xCA 0xFE 0xBA 0xBE

字符串:java/, javax/ 等包名前缀

操作系统相关

Windows API

Registry Hive:regf 签名

错误码:ERROR_ACCESS_DENIED, ERROR_FILE_NOT_FOUND 等

GUID/CLSID:特定的COM类标识符,格式为 {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}

Linux / Unix

配置文件路径:/etc/passwd, /etc/shadow

系统调用号:特定的系统调用数字

游戏开发相关

Unreal Engine

Package 文件:以 0x9E2A83C1 开头

字符串:CoreUObject, Engine 等命名空间

其他常见库

Boost C++ 库

Boost.Serialization 会在序列化数据中包含版本信息

Protobuf (Google Protocol Buffers)

Wire Type:字段编码中的特定常量(如 0x08 对应 Varint)

JSON 解析器

字符串:虽然JSON本身是标准,但特定库( RapidJSON, jsoncpp)有其独特的错误消息或调试字符串

使用方法:

  1. 在IDA中按ShiftF12查看所有字符串,寻找上述特征字符串

  2. 使用二进制编辑器或IDA查看文件开头和特定偏移处的魔数

  3. 找到特征常量后,按X查看哪些代码引用了它,快速定位核心逻辑

  4. ida里面如果函数对常数进行了复杂的计算(纯运算),可以查看代码是否写了特征常量

输入的str->考虑flag,在IDA里面进行改名

输入明文数据 -> AES 加密 -> HEX 编码 -> 与目标串比较

目标字符串 -> HEX 解码 -> AES 解密 -> 明文数据

需要用到的第三方库: PyCrypto >> pip install pycrypto

*shift/可以加注释

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询