IDA中加密算法特征与解密
Base64
转换步骤:
- 将待转换的字符串,每 3 个字节分为一组,每个字节占 8 bit,共 24 个二进制位
- 将上面的 24 个二进制位,每 6 个字节做为一组,共分为 4 组 (若最后一组字符数不足三个,用 ‘=’ 补充)
- 在每组前面添加两个 0,每组由 6 个变为 8 个二进制位,总共 32 个二进制位,即 4 个字节
- 根据 Base64 编码对照表获得对应的值
Base64 算法解码过程
去掉所有的等号,查表将字符转为二进制的索引值,最后每 8 位一组计算 ASCii 码还原字符,不足 8 位则丢弃原始 Base64 码表:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
Base64 & Base32 & Base16
Base64 就是用每6 位二进制(2 的 6 次幂就是 64)
来表示一个字符
Base32 就是用每5 位二进制(2 的 5 次幂就是 32)
来表示一个字符
Base16 就是用每4 位二进制(2 的 4 次幂就是 16)
来表示一个字符问:Base64 为什么使用 3 个字节作为一组呢?
因为 6 和 8 的最小公倍数为 24,三个字节正好 24 个二进制位,每 6 bit 为一组,恰好能够分为 4 组
特点
Base64 要用到 Base64 码表,可以在程序中找到连续的字符串:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
根据 Base64 加密原理,代码中必然存在根据余数个数判断是否添加等号的代码
字符'='
的 ASCii 码:61(0x3D),也有可能直接索引码表里面的'='
识别代码中对数据的左移右移操作
((a[0] & 3) << 4) | (a[1] >> 4 )
和(16 * (a[0] & 3)) | (a[1] / 16)
是等价操作,都表示取a[0]
后 2 位与a[1]
前 4 位拼接,是 Base64 中的常见操作最主要的是理解编码解码原理,比如编码时通常都会用 3 个字节一组来处理比特位数据
原理
以下图的表格为示例
具体分析一下整个过程:
- 第 1 步,根据
'M'
、'a'
、'n'
对应的 ASCii 码值分别为 77,97,110,对应的二进制值是:01001101、01100001、01101110,由此组成一个 24 位的二进制字符串 - 第 2 步,如图红色框,将 24 位每 6 位二进制位一组分成 4 组
- 第 3 步,在上面每一组前面补两个 0,扩展成 32 个二进制位:00010011、00010110、00000101、00101110
- 第 4 步,四组 8bit 分别对应的值 (Base64 编码索引) 为:19、22、5、46,在 Base64 编码表中进行查找,分别对应:
'T'
、'W'
、'F'
、'u'
,因此 “Man” 经过 Base64 编码之后就变为:"TWFu"
如果遇到位数不足的情况,位数不足用 ‘=’ 补充,总共有两种情况:
- 最后一组只有一个字符
- 最后一组有两个字符
加解密代码
Python 版(简洁脚本)
特点
- 可以更换加密的码表
- 快捷,直接使用即可
代码
import base64
# enc表示更换码表后的待解密字符串
enc = "x2dtJEOmyjacxDemx2eczT5cVS9fVUGvWTuZWjuexjRqy24rV29q"
# new_table表示更换后的码表
new_table = "ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/"
# old_table表示原始码表
old_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# 将enc还原成原始码表加密后的内容,存放在dec中
dec = enc.translate(str.maketrans(new_table, old_table))
# Base64解密,base64.b64decode()的结果为 bytes 类型
print(base64.b64decode(dec))
Python 版(完整系统)
特点
- 可以更换加密的码表
- 支持加密和解密
代码
# coding:utf-8
s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" # 原始码表
def my_base64_encode(inputs):
try:
# 将字符串转化为2进制
bin_str = []
for i in inputs:
x = str(bin(ord(i))).replace('0b', '')
bin_str.append('{:0>8}'.format(x))
# print(bin_str)
# 输出的字符串
outputs = ""
# 不够三倍数,需补齐的次数
nums = 0
while bin_str:
# 每次取三个字符的二进制
temp_list = bin_str[:3]
if len(temp_list) != 3:
nums = 3 - len(temp_list)
while len(temp_list) < 3:
temp_list += ['0' * 8]
temp_str = "".join(temp_list)
# print(temp_str)
# 将三个8字节的二进制转换为4个十进制
temp_str_list = []
for i in range(0, 4):
temp_str_list.append(int(temp_str[i * 6:(i + 1) * 6], 2))
# print(temp_str_list)
if nums:
temp_str_list = temp_str_list[0:4 - nums]
for i in temp_str_list:
outputs += s[i]
bin_str = bin_str[3:]
outputs += nums * '='
print("加密完成:\n%s " % outputs)
except Exception as e:
print(f"加密错误: \n{e}")
def my_base64_decode(inputs):
try:
# 将字符串转化为2进制
bin_str = []
for i in inputs:
if i != '=':
x = str(bin(s.index(i))).replace('0b', '')
bin_str.append('{:0>6}'.format(x))
# print(bin_str)
# 输出的字符串
outputs = ""
nums = inputs.count('=')
while bin_str:
temp_list = bin_str[:4]
temp_str = "".join(temp_list)
# print(temp_str)
# 补足8位字节
if (len(temp_str) % 8 != 0):
temp_str = temp_str[0:-1 * nums * 2]
# 将四个6字节的二进制转换为三个字符
for i in range(0, int(len(temp_str) / 8)):
outputs += chr(int(temp_str[i * 8:(i + 1) * 8], 2))
bin_str = bin_str[4:]
print("解密完成:\n%s " % outputs)
except Exception as e:
print(f"解密错误: \n{e}")
print(" 可更换码表的 Base64 加解密系统 ")
print("*************************************")
select = input("是否更换加密的码表? (y or n) 你的选择: ")
if select == "y" or select == "yes":
s = input("在这里输入码表: ")
print(".....done, 已更改!")
else:
print("码表未做更改!")
input_str = input("输入数据: ")
print("*************************************")
my_base64_encode(input_str)
print("*************************************")
my_base64_decode(input_str)
print("*************************************", end='')
C++ 版(完整系统)
特点
- 可以更换加密的码表
- 支持加密和解密
代码
#include <stdio.h>
#include <string.h>
// Base64 字符表
char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// 编码一个字节
char encode_byte(unsigned char b) {
return base64chars[b];
}
// Base64 编码
void base64_encode(const char *input, size_t length) {
for (size_t i = 0; i < length; i += 3) {
// 取三个字节
unsigned int triplet = (input[i] << 16) | (i + 1 < length ? input[i + 1] << 8 : 0) | (i + 2 < length ? input[i + 2] : 0);
// 分割成四个六位字节并编码
printf("%c%c%c%c",
encode_byte((triplet >> 18) & 0x3F),
encode_byte((triplet >> 12) & 0x3F),
i + 1 < length ? encode_byte((triplet >> 6) & 0x3F) : '=',
i + 2 < length ? encode_byte(triplet & 0x3F) : '=');
}
printf("\n");
}
// 映射关系
int decode_char(char ch) {
for (int i = 0; i < 64; i++) {
if (base64chars[i] == ch) {
return i;
}
}
return 0;
}
// Base64 解码
void base64_decode(const char *input, size_t length) {
for (size_t i = 0; i < length; i += 4) {
// 取四个 Base64 字符
unsigned int quad = (decode_char(input[i]) << 18) | (decode_char(input[i + 1]) << 12) |
(decode_char(i + 2 < length ? input[i + 2] : '=') << 6) |
(decode_char(i + 3 < length ? input[i + 3] : '='));
// 分割成三个字节并输出
printf("%c%c%c",
(quad >> 16) & 0xFF,
i + 2 < length ? (quad >> 8) & 0xFF : 0,
i + 3 < length ? quad & 0xFF : 0);
}
printf("\n");
}
void start() {
printf(" 可更换码表的 Base64 加解密系统 \n");
printf("*************************************\n");
char option;
printf("是否更换加密的码表? (y or n) 你的选择: ");
fflush(stdout);
scanf("%c", &option);
if(option == 'y' || option == 'Y') {
printf("在这里输入新的码表: ");
fflush(stdout);
scanf("%s", base64chars);
printf(".....done, 已更改!\n");
} else
printf("码表未做更改!\n");
}
int main() {
start();
char data[256];
printf("输入数据: ");
fflush(stdout);
scanf("%s", data);
size_t input_length = strlen(data);
printf("*************************************\n");
printf("加密结果: \n");
base64_encode(data, input_length);
printf("*************************************\n");
printf("解密结果: \n");
base64_decode(data, input_length);
printf("*************************************");
return 0;
}
IDA 示例
char *__fastcall base64_encode(char *a1)
{
int v1; // eax
int v2; // eax
int v3; // eax
int v4; // eax
int v5; // eax
int v6; // eax
int v8; // [rsp+1Ch] [rbp-54h]
int v9; // [rsp+20h] [rbp-50h]
int v10; // [rsp+24h] [rbp-4Ch]
int v11; // [rsp+24h] [rbp-4Ch]
int v12; // [rsp+24h] [rbp-4Ch]
int v13; // [rsp+28h] [rbp-48h]
int v14; // [rsp+2Ch] [rbp-44h]
char src[56]; // [rsp+30h] [rbp-40h]
unsigned __int64 v16; // [rsp+68h] [rbp-8h]
v16 = __readfsqword(0x28u);
v1 = strlen(a1); // a1为输入的字符串
v14 = v1 % 3; // v14为输入字符串长度除3以后的余数
v13 = v1 / 3; // v13为3个一组的字符组合数量
memset(src, 0, 0x30uLL);
v10 = 0;
v8 = 0;
v9 = 0;
while ( v8 < v13 )
{
v2 = v10;
v11 = v10 + 1;
// 第一个:第一个字符右移2位,取前6位作为索引值,查找对应字符
src[v2] = base64_table[a1[v9] >> 2];
v3 = v11++;
// 第二个:第一个字符取后2位与第二个字符的前4位拼接
src[v3] = base64_table[(16 * (a1[v9] & 3)) | (a1[v9 + 1] >> 4)];
// 第三个:第二个字符取后4位与第三个字符的前2位拼接,查找对应字符
src[v11] = base64_table[(4 * (a1[v9 + 1] & 0xF)) | (a1[v9 + 2] >> 6)];
v4 = v11 + 1;
v10 = v11 + 2;
// 第四个:第三个字符取后6位作为索引,查找对应字符
src[v4] = base64_table[a1[v9 + 2] & 0x3F];
v9 += 3;
++v8;
}
if ( v14 == 1 )
{ // 余数为1,则需要添加两个等号
src[v10] = base64_table[a1[v9] >> 2];
src[v10 + 1] = base64_table[16 * (a1[v9] & 3)];
strcat(src, "==");
}
else if ( v14 == 2 )
{ // 余数为2,则需要添加1个等号
v5 = v10;
v12 = v10 + 1;
src[v5] = base64_table[a1[v9] >> 2];
v6 = v12++;
src[v6] = base64_table[(16 * (a1[v9] & 3)) | (a1[v9 + 1] >> 4)];
src[v12] = base64_table[4 * (a1[v9 + 1] & 0xF)];
src[v12 + 1] = '=';
}
strcpy(a1, src);
return a1;
}
RC4
RC4 是对称加密算法,通过密钥 key 和 S 盒生成密钥流,明文逐字节异或 S 盒,同时 S 盒也会发生改变
加密与解密使用了相同的函数和密钥 K,加密的强度主要来源于密钥的安全性,密钥泄露能直接解密出明文相关 Writeup 见 《【攻防世界】crypt》、《【攻防世界】ereere》
特点
RC4 加密算法属于流加密算法,包括初始化函数和加解密函数
初始化函数中有两个 256 循环,第一个循环给 s 盒初始化为 0 - 255,第二个循环根据密钥 key 对 s 盒 swap
加解密函数中有一个 256 循环,使明文和 s 盒异或生成密文
原理
初始化部分
初始化长度为 256 的 S 盒。第一个 for 循环将 0 到 255 的互不重复的元素装入 S 盒;第二个 for 循环根据密钥 key 打乱 S 盒,i 确保 S-box 的每个元素都得到处理,j 保证 S-box 的搅乱是随机的
不同的 S-box 在经过伪随机子密码生成算法的处理后可以得到不同的子密钥序列,将 S-box 和明文进行 xor 运算,得到密文,解密过程也完全相同
void rc4_init(unsigned char *s, unsigned char *key, unsigned long key_Len)
{
int i = 0, j = 0;
unsigned char k[256] = {0}; //临时向量 k
unsigned char tmp = 0;
for(i = 0; i < 256; i++) {
s[i] = i;
k[i] = (unsigned char) key[i % key_Len]; //Len = strlen(key),密钥的长度
}
for(i = 0; i < 256; i++) { //打乱s表
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j]; //交换s[i]和s[j]
s[j] = tmp;
}
}
加解密部分
每收到一个字节,就进行循环。通过一定的算法定位 S 盒中的一个元素,并与输入字节异或,得到 k;同时,循环中还改变了 S 盒
如果输入的是明文,输出的就是密文;如果输入的是密文,输出的就是明文
void rc4_crypt(unsigned char *s, unsigned char *Data, unsigned long Data_Len)
{
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for(k = 0; k < Data_Len; k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j]; //交换s[i]和s[j]
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] ^= s[t];
}
}
加解密代码
Python 版(简洁脚本)
特点
- 利用 Python 库快捷实现 RC4 算法
Key
表示 RC4 的密钥,Str
表示待加解密的内容 (bytes 型)
代码
from Crypto.Cipher import ARC4
Str = b''
Key = ""
flag = ARC4.new(bytes(Key, encoding='utf-8')).decrypt(Str)
print(flag)
- 示例
Str = [100, 217, 79, 52, 168, 29, 108, 198, 213, 33, 14, 114, 152, 198, 117, 185, 143] # 待加解密的内容
Key = "SecretKey" # 密钥key
Str = bytes(Str)
''' 输出:
b'password123456789'
'''
Python 版(具体实现)
特点
- 包含具体实现流程
代码
Str = "" # 待加解密的内容
Key = "" # 密钥key
flag = "" # 存放加解密后的结果
# ---------- rc4_init ----------
s_box = [] # 定义 s 盒
for i in range(256): # 生成初始 s 盒
s_box.append(i)
# T[i] = K[i mod len(Key)] # 这个算法里没有 T[i],下面会解释
t = 0
j = 0
for i in range(256): # 打乱 s 盒顺序
tmp = s_box[i]
j = (j + s_box[i] + ord(Key[t])) & 0xff # j = (j + S[i] + T[i]) mod 256
s_box[i] = s_box[j]
s_box[j] = tmp
t = t + 1 # 这里引入的 t 加一个 if 条件其实就是为了做 t = i % len(Key)
if t >= len(Key): # Key[t] 配合 t = i % len(Key) 就是实现了 T[i] = K[i mod len(Key)]
t = 0 # 小细节写法不同而已,大致思路是一样的
# ---------- rc4_crypt ----------
i = 0
j = 0
for k in range(len(Str)):
i = (i + 1) & 0xff
j = (j + s_box[i]) & 0xff # & 0xff 是为了做 % 256,两者效果相同
tmp = s_box[i]
s_box[i] = s_box[j]
s_box[j] = tmp
t = (s_box[i] + s_box[j]) & 0xff # & 0xff 是为了做 % 256,两者效果相同
flag += chr(Str[k] ^ s_box[t]) # 明文异或得密文,密文异或得明文
print("解密结果: " + flag)
- 示例
Str = [100, 217, 79, 52, 168, 29, 108, 198, 213, 33, 14, 114, 152, 198, 117, 185, 143] # 待加解密的内容
Key = "SecretKey" # 密钥key
flag = "" # 存放加解密后的结果
''' 输出:
解密结果: password123456789
'''
C++ 版(具体实现)
特点
- 包含具体实现流程
代码
#include <stdio.h>
void rc4_init(unsigned char *s, unsigned char *key, unsigned long key_Len)
{
int i = 0, j = 0;
unsigned char k[256] = {0}; //临时向量 k
unsigned char tmp = 0;
for(i = 0; i < 256; i++) {
s[i] = i;
k[i] = (unsigned char) key[i % key_Len]; //Len = strlen(key),密钥的长度
}
for(i = 0; i < 256; i++) { //打乱s表
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j]; //交换s[i]和s[j]
s[j] = tmp;
}
}
void rc4_crypt(unsigned char *s, unsigned char *Data, unsigned long Data_Len)
{
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for(k = 0; k < Data_Len; k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j]; //交换s[i]和s[j]
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] ^= s[t];
}
}
- 示例一
#include <string.h>
int main()
{
unsigned char data[] = "password123456789";
unsigned char key[] = "SecretKey"; // 密钥 key
// 获取密钥长度
unsigned long key_len = strlen((char *)key);
// 初始化 RC4 状态向量
unsigned char state[256];
rc4_init(state, key, key_len);
// 加密数据
rc4_crypt(state, data, strlen((char *)data));
printf("加密后: %s\n", data);
// 重新初始化 RC4 状态向量
rc4_init(state, key, key_len);
// 解密数据
rc4_crypt(state, data, strlen((char *)data));
printf("解密后: %s\n", data);
return 0;
}
/* 输出:
加密后: d貽4�l普!r樒u箯
解密后: password123456789
*/
- 示例二
#include <string.h>
int main()
{
unsigned char data[] = {0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39};
unsigned char key[] = "SecretKey"; // 密钥 key
// 获取数据长度
unsigned long data_len = sizeof(data) / sizeof(data[0]);
// 获取密钥长度
unsigned long key_len = strlen((char *)key);
// 初始化 RC4 状态向量
unsigned char state[256];
rc4_init(state, key, key_len);
// 加密数据
rc4_crypt(state, data, data_len);
printf("加密后: %s\n", data);
// 重新初始化 RC4 状态向量
rc4_init(state, key, key_len);
// 解密数据
rc4_crypt(state, data, data_len);
printf("解密后: %s\n", data);
return 0;
}
/* 输出:
加密后: d貽4�l普!r樒u箯D{�
解密后: password123456789D{�
*/
IDA 示例
__int64 __fastcall rc4_init(_DWORD *a1, __int64 a2, int a3)
{
__int64 result; // rax
int i; // [rsp+0h] [rbp-28h]
int j; // [rsp+0h] [rbp-28h]
int v6; // [rsp+4h] [rbp-24h]
int v7; // [rsp+8h] [rbp-20h]
int v8; // [rsp+Ch] [rbp-1Ch]
_DWORD *v9; // [rsp+10h] [rbp-18h]
*a1 = 0;
a1[1] = 0;
v9 = a1 + 2;
for ( i = 0; i < 256; ++i )
v9[i] = i; //循环给 s 盒赋值
v6 = 0;
result = 0i64;
LOBYTE(v7) = 0;
for ( j = 0; j < 256; ++j ) //循环根据密钥 key 对 s 盒进行 swap
{ //Ⅰ、Ⅱ、Ⅲ 交换v9[j]和v9[v7]的值
v8 = v9[j]; //Ⅰ
v7 = (*(a2 + v6) + v8 + v7);
v9[j] = v9[v7]; //Ⅱ
v9[v7] = v8; //Ⅲ
if ( ++v6 >= a3 )
v6 = 0;
result = (j + 1);
}
return result;
}
_DWORD *__fastcall rc4_crypt(_DWORD *a1, __int64 a2, int a3)
{
_DWORD *result; // rax
int i; // [rsp+0h] [rbp-28h]
int v5; // [rsp+4h] [rbp-24h]
int v6; // [rsp+8h] [rbp-20h]
int v7; // [rsp+Ch] [rbp-1Ch]
int v8; // [rsp+10h] [rbp-18h]
_DWORD *v9; // [rsp+18h] [rbp-10h]
v5 = *a1;
v6 = a1[1];
v9 = a1 + 2;
for ( i = 0; i < a3; ++i )
{ //Ⅰ、Ⅱ、Ⅲ、Ⅳ 交换v9[v5]和v9[v6]的值
v5 = (v5 + 1); //Ⅰ
v7 = v9[v5];
v6 = (v7 + v6);
v8 = v9[v6]; //Ⅱ
v9[v5] = v8; //Ⅲ
v9[v6] = v7; //Ⅳ
*(a2 + i) ^= LOBYTE(v9[(v8 + v7)]);
}
*a1 = v5;
result = a1;
a1[1] = v6;
return result;
}
TEA
TEA 算法全称微型加密算法,最初是由剑桥计算机实验室的 David Wheeler 和 Roger Needham 在 1994 年设计的。 TEA 算法使用 64 位的明文分组和 128 位的密钥,它使用 Feistel 分组加密框架,需要进行 64 轮迭代,尽管作者认为 32 轮已经足够了,32 轮迭代加密后得到的密文就是 64 位
该算法使用了一个神秘常数
δ(Delta)
作为倍数,它来源于黄金比率,以保证每一轮加密都不相同。但δ(Delta)
的精确值似乎并不重要,这里 TEA 把它定义为δ =「(√5 - 1)231」
(也就是程序中的0x9e3779b9
)相关 Writeup 见 《【GDOUCTF 2023】Tea》
特点
密钥为 128 位 (一般分为 4 个 32 位子密钥),明文为 64 位 (一般分为 2 个 32 位明文),主要做了 32 轮变换,每轮变换中都涉及移位和变换操作
TEA 算法中有一个固定的常数
δ(Delta)
,通常为0x9e3779b9
或者0x61c88647
,当然也可能魔改有对常数
δ(Delta)
进行的操作,在 TEA 算法加密过程中,δ = 0x9e3779b9
时代码中会出现累加,δ = 0x61c88647
时代码中会出现累减 (也就是说,加密过程中:sum -= 0x61c88647
和sum += 0x9e3779b9
,这两个值是等价的)当
δ = 0x9e3779b9
时,解密的初始值sun = num_rounds * DELTA
;当δ = 0x61c88647
时,解密的初始值sun = - num_rounds * DELTA
涉及到的移位操作通常是左移 4 位、右移 5 位
在 TEA 算法中取密钥 key 的时候是固定下标取的
原理
- 将明文按 64 位(8 字节)分组,每组视为一个 64 位的二进制数,同时将这 64 位的二进制数分成两个相等长度的部分
v0
和v1
(两个 32 位明文数据) - 将 128 位的密钥分成 4 个 32 位的子密钥,分别记为
k0、k1、k2、k3
(四个 32 位密钥) - 设定一个 32 位的常数 delta,其值为
0x9E3779B9
,(也可以为0x61c88647
) - 定义 32 轮加密迭代,每轮中右半部分会经过一个运算,包括左移、异或、加等操作,并且每轮的密钥是不同的
- 最后得到经过加密算法加密的密文
v0'
和v1'
加密流程如图:
加解密代码
C++ 版
特点
- 包含具体实现流程
- 支持快速修改加密轮数
num_rounds
和delta
的值
代码
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9 // delta值
//加密函数
void tea_encrypt(unsigned int num_rounds, uint32_t* v, uint32_t* key)
{
uint32_t v0 = v[0], v1 = v[1]; // 初始化参数
uint32_t sum = 0; // 初始sum为0
uint32_t k0 = key[0], k1 = key[1], k2 = key[2], k3 = key[3]; // key值
for (int i = 0; i < num_rounds; i++) { // 一般为32轮加密
sum += DELTA;
v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
}
v[0] = v0; v[1] = v1; // 将结果存回原密钥数组
}
//解密函数
void tea_decrypt(unsigned int num_rounds, uint32_t* v, uint32_t* key)
{
uint32_t v0 = v[0], v1 = v[1]; // 初始化参数
uint32_t sum = num_rounds * DELTA; // 初始sum为delta*加密轮数, 如果dalta为0x9e3779b9,加密32轮,则这个值为0xC6EF3720
uint32_t k0 = key[0], k1 = key[1], k2 = key[2], k3 = key[3]; // key值
for (int i = 0; i < num_rounds; i++) { // 一般为32轮解密
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= DELTA;
}
v[0] = v0; v[1] = v1;
}
- 示例一
int main()
{
uint32_t data[] = {0xDEADBEEF, 0xFACEB00C, 0xDAC0FFEE, 0xABADBABE};
uint32_t key[] = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
unsigned int r = 32; // 轮数
int data_len = sizeof(data) / sizeof(data[0]); // data 的长度
for (int i = 0; i < data_len; i = i + 2) // 2 个 4 字节为一组
tea_encrypt(r, &data[i], key);
printf("加密后:0x%x 0x%x 0x%x 0x%x \n", data[0], data[1], data[2], data[3]);
for (int i = 0; i < data_len; i = i + 2) // 2 个 4 字节为一组
tea_decrypt(r, &data[i], key);
printf("解密后:0x%x 0x%x 0x%x 0x%x \n", data[0], data[1], data[2], data[3]);
return 0;
}
/* 输出:
加密后:0x99ad7ec2 0x938c4882 0x10ad3961 0xe971209e
解密后:0xdeadbeef 0xfaceb00c 0xdac0ffee 0xabadbabe
*/
- 示例二
#include <cstring>
int main()
{
char data[] = "password123456789";
uint32_t key[] = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
unsigned int r = 32; // 轮数
int data_len = strlen(data); // data 的长度
for (int i = 0; i < data_len; i = i + 8) // 8 字节为一组
tea_encrypt(r, (uint32_t*) &data[i], key);
printf("加密后:%s\n", data);
for (int i = 0; i < data_len; i = i + 8) // 8 字节为一组
tea_decrypt(r, (uint32_t*) &data[i], key);
printf("解密后:%s\n", data);
return 0;
}
/* 输出:
加密后:(乱码)
解密后:password123456789
*/
TEA 算法可能的魔改:
- 修改 delta 值,不再为 0x9e3779b9 或者 0x61c88647
- 修改加密过程,在每一轮迭代中增加可逆运算,例如加上一个
^ (sum + i)
:for (int i = 0; i < num_rounds; i++) { // 一般为 32 轮加密 sum += DELTA; v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1) ^ (sum + i); v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3) ^ (sum + i); }
那么解密时,则修改为:
for (int i = 0; i < num_rounds; i++) { // 一般为32轮解密 v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3) ^ (sum + (num_rounds - 1 - i)); v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1) ^ (sum + (num_rounds - 1 - i)); sum -= DELTA; }
- 在最后赋值时,添加可逆运算:
v[0] = v0 ^ 0x12; v[1] = v1 ^ 0x34;
IDA 示例
__int64 __fastcall tea_encrypt(unsigned int a1, unsigned int *a2, unsigned int *a3)
{
__int64 result; // rax
unsigned int v4; // [rsp+18h] [rbp-20h]
unsigned int v5; // [rsp+1Ch] [rbp-1Ch]
int v6; // [rsp+20h] [rbp-18h]
unsigned int i; // [rsp+24h] [rbp-14h]
v4 = *a2;
v5 = a2[1];
v6 = 0;
for ( i = 0; a1 > i; ++i )
{
v6 -= 1640531527; // 即:0x61C88647
v4 += (v5 + v6) ^ (16 * v5 + *a3) ^ ((v5 >> 5) + a3[1]);
v5 += (v4 + v6) ^ (16 * v4 + a3[2]) ^ ((v4 >> 5) + a3[3]);
}
*a2 = v4;
result = v5;
a2[1] = v5;
return result;
}
XTEA
由于 TEA 算法被发现存在缺陷,作为回应,设计者提出了一个 TEA 的升级版本——XTEA
XTEA 是 TEA 的扩展,也称做 TEAN,它使用与 TEA 相同的简单运算,同样是一个 64 位块的 Feistel 密码,使用 128 位密钥,建议 64 轮,但四个子密钥采取不正规的方式进行混合以阻止密钥表攻击
特点
基本上 TEA 的加密特点 XTEA 也都有,例如:存在 DELTA 值
同样,涉及到的移位操作通常是左移 4 位、右移 5 位;不同的是,对于
sum
的累加或累减操作位于v0
、v1
两个数据处理之间与 TEA 不同的是,在 XTEA 中密钥 key 的下标是通过计算得来的,分别是
key[sum & 3]
和key[(sum >> 11) & 3]
原理
与 TEA 的思想基本类似,对于密钥 key 的取法与 TEA 有所不同
加密流程如图:
加解密代码
C++ 版
特点
- 包含具体实现流程
- 支持快速修改加密轮数
num_rounds
和delta
的值
代码
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9 // DELTA值
//加密函数
void xtea_encrypt(unsigned int num_rounds, uint32_t* v, uint32_t* key)
{
uint32_t v0 = v[0], v1 =v [1];
uint32_t sum = 0;
for (int i = 0; i < num_rounds; i++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
sum += DELTA;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
}
v[0] = v0; v[1] = v1;
}
//解密函数
void xtea_decrypt(unsigned int num_rounds, uint32_t* v, uint32_t* key)
{
uint32_t v0 = v[0], v1 = v[1];
uint32_t sum = DELTA * num_rounds;
for (int i = 0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
sum -= DELTA;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0] = v0; v[1] = v1;
}
- 示例一
int main()
{
uint32_t data[]={
0x168F8672, 0x02DBD824, 0xCF647FCA, 0xE6EFA7EF, 0x4AE016F0, 0xC5832E1D, 0x455C0A05, 0xFFEB8140,
0xBE9561EF, 0x7F819E23, 0x3BC04269, 0xC68B825B, 0xE6A5B1F0, 0xBD03CBBD, 0xA9B3CE0E, 0x6C85E6E7,
0x9F5C71EF, 0x3BE4BD57
};
uint32_t key[] = {0xDEADBEEF, 0x87654321, 0xFACEB00C, 0xCAFEBABE};
unsigned int r = 32; // 轮数
int data_len = sizeof(data) / sizeof(data[0]); // data 的长度
printf("解密后:");
for (int i = 0; i < data_len; i = i + 2) { // 2 个 4 字节为一组
xtea_decrypt(r, &data[i], key);
printf("%s", &data[i]); // 这种方式输出无需转换小端序
}
printf("加密后:");
for (int i = 0; i < data_len; i = i + 2) { // 2 个 4 字节为一组
xtea_encrypt(r, &data[i], key);
printf("%s", &data[i]);
}
return 0;
}
/* 输出:
解密后:DASCTF{Don't_forget_to_drink_tea}
加密后:(乱码)
*/
- 示例二
#include <cstring>
int main()
{
char data[] = "DASCTF{Don't_forget_to_drink_tea}";
uint32_t key[] = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
unsigned int r = 32; // 轮数
int data_len = strlen(data); // data 的长度
printf("加密后:\n");
for (int i = 0; i < data_len; i = i + 8) // 8 字节为一组
xtea_encrypt(r, (uint32_t*) &data[i], key);
printf("%s\n", data);
printf("解密后:\n");
for (int i = 0; i < data_len; i = i + 8) // 8 字节为一组
xtea_decrypt(r, (uint32_t*) &data[i], key);
printf("%s\n", data);
return 0;
}
/* 输出:
加密后:(乱码)
解密后:DASCTF{Don't_forget_to_drink_tea}
*/
IDA 示例
__int64 __fastcall encode(unsigned int a1, unsigned int *a2, __int64 a3)
{
__int64 v3; // r11
__int64 result; // rax
unsigned int v5; // [rsp+8h] [rbp-28h]
unsigned int v6; // [rsp+Ch] [rbp-24h]
unsigned int v7; // [rsp+10h] [rbp-20h]
unsigned int i; // [rsp+14h] [rbp-1Ch]
v7 = *a2;
v6 = a2[1];
v5 = 0; // 初始 sum 为 0
for ( i = 0; i < a1; ++i ) // a1 为迭代轮数
{
v7 += ~(*(_DWORD *)(a3 + 4LL * ~(~v5 | 0xFFFFFFFC)) + v5) & (v6 + (~(v6 >> 5) & (16 * v6) | ~(16 * v6) & (v6 >> 5))) | ~(v6 + (~(v6 >> 5) & (16 * v6) | ~(16 * v6) & (v6 >> 5))) & (*(_DWORD *)(a3 + 4LL * ~(~v5 | 0xFFFFFFFC)) + v5);
v5 -= 1640531527; // 即:0x61C88647
v3 = (v5 >> 11) & ((v5 >> 11) ^ 0xFFFFFFFC);
v6 += ~(*(_DWORD *)(a3 + 4 * v3) + v5) & (v7 + (~(v7 >> 5) & (16 * v7) | ~(16 * v7) & (v7 >> 5))) | ~(v7 + (~(v7 >> 5) & (16 * v7) | ~(16 * v7) & (v7 >> 5))) & (*(_DWORD *)(a3 + 4 * v3) + v5);
}
*a2 = v7; // 将加密后的结果写回原数组
result = v6;
a2[1] = v6; // 将加密后的结果写回原数组
return result;
}
XXTEA
XXTEA,又称 Corrected Block TEA,是 XTEA 的升级版 ,支持块加密,设计者是 Roger Needham、David Wheeler,由于其代码实现非常简单,运算也是由异或等基本操作组成的,因此非常适合计算和存储能力吃紧的设备中使用
XXTEA 是一个非平衡 Feistel 网络分组密码,在可变长度块上运行,这些块是 32 位大小的任意倍数(最小 64 位),使用 128 位密钥,是目前 TEA 系列中最安全的算法,但性能较上两种有所降低
特点
分组长度可变,是任意 32 bit 为步长递增,最少 64 bit 的二进制字符串
密钥长度仍然为 128 位
加密轮数可变,取决于分组长度,分组越长轮数越少(最低 6 轮),分组越短轮数越多(最多 32 个完整轮数),
加密轮数 = 明文总字节数 / 8
存在 DELTA 值
原理
加解密代码
C++ 版
特点
- 包含具体实现流程
- 支持快速修改
delta
的值
代码
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9 // DELTA值
//加密函数
void xxtea_encrypt(unsigned int num_rounds, uint32_t* v, uint32_t* key)
{
uint32_t y, z, sum;
uint32_t p, rounds, e;
rounds = 6 + 52 / num_rounds;
sum = 0;
z = v[num_rounds - 1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < num_rounds - 1; p++)
{
y = v[p + 1];
z = v[p] += (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)));
}
y = v[0];
z = v[num_rounds - 1] += (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)));
} while (--rounds);
}
//解密函数
void xxtea_decrypt(unsigned int num_rounds, uint32_t* v, uint32_t* key)
{
uint32_t y, z, sum;
uint32_t p, rounds, e;
rounds = 6 + 52 / num_rounds;
sum = rounds * DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p = num_rounds - 1; p > 0; p--)
{
z = v[p - 1];
y = v[p] -= (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)));
}
z = v[num_rounds - 1];
y = v[0] -= (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)));
sum -= DELTA;
} while (--rounds);
}
- 示例一
int main()
{
uint32_t data[] = {0xDEADBEEF, 0xFACEB00C, 0xDAC0FFEE, 0xABADBABE};
uint32_t key[] = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
int data_len = sizeof(data) / sizeof(data[0]); // data 的长度
unsigned int r = sizeof(data) / 8; // 轮数
for (int i = 0; i < data_len; i = i + 2) // 2 个 4 字节为一组
xxtea_encrypt(r, &data[i], key);
printf("加密后:0x%x 0x%x 0x%x 0x%x\n", data[0], data[1], data[2], data[3]);
for (int i = 0; i < data_len; i = i + 2) // 2 个 4 字节为一组
xxtea_decrypt(r, &data[i], key);
printf("解密后:0x%x 0x%x 0x%x 0x%x\n", data[0], data[1], data[2], data[3]);
return 0;
}
/* 输出:
加密后:0xf1560f7a 0x17cf17fa 0xf69c6e0c 0x3d09333e
解密后:0xdeadbeef 0xfaceb00c 0xdac0ffee 0xabadbabe
*/
int main()
{
uint32_t data[] = {0xDEADBEEF, 0xFACEB00C, 0xDAC0FFEE, 0xABADBABE};
uint32_t key[] = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
unsigned int r = sizeof(data) / 8; // 轮数
// 加密
xxtea_encrypt(r, data, key);
printf("加密后:0x%x 0x%x 0x%x 0x%x\n", data[0], data[1], data[2], data[3]);
printf("加密后:%s\n", data);
// 解密
xxtea_decrypt(r, data, key);
printf("解密后:0x%x 0x%x 0x%x 0x%x\n", data[0], data[1], data[2], data[3]);
printf("解密后:%s\n", data);
return 0;
}
/* 输出:
加密后:0xf1560f7a 0x17cf17fa 0xdac0ffee 0xabadbabe
加密后:(乱码)
解密后:0xdeadbeef 0xfaceb00c 0xdac0ffee 0xabadbabe
解密后:(乱码)
*/
- 示例二
#include <cstring>
int main()
{
char data[] = "password123456789";
uint32_t key[] = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
int data_len = strlen(data); // data 的长度
unsigned int r = strlen(data) / 8; // 轮数
printf("加密后:");
for (int i = 0; i < data_len; i = i + 8) // 8 字节为一组
xxtea_encrypt(r, (uint32_t*) &data[i], key);
printf("%s\n", data);
printf("解密后:");
for (int i = 0; i < data_len; i = i + 8) // 8 字节为一组
xxtea_decrypt(r, (uint32_t*) &data[i], key);
printf("%s\n", data);
return 0;
}
/* 输出:
加密后:(乱码)
解密后:password123456789
*/
#include <cstring>
int main()
{
char data[] = "password123456789";
uint32_t key[] = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
unsigned int r = strlen(data) / 8; // 轮数
xxtea_encrypt(r, (uint32_t*) &data, key);
printf("加密后:%s\n", data);
xxtea_decrypt(r, (uint32_t*) &data, key);
printf("解密后:%s\n", data);
return 0;
}
/* 输出:
加密后:(乱码)123456789
解密后:password123456789
*/
IDA 示例
__int64 __fastcall xxtea_encrypt(unsigned int a1, _DWORD *a2, __int64 a3)
{
unsigned int *v3; // rax
_DWORD *v4; // rax
__int64 result; // rax
unsigned int v6; // [rsp+20h] [rbp-18h]
unsigned int v7; // [rsp+24h] [rbp-14h]
unsigned int i; // [rsp+28h] [rbp-10h]
unsigned int v9; // [rsp+2Ch] [rbp-Ch]
int v10; // [rsp+30h] [rbp-8h]
unsigned int v11; // [rsp+34h] [rbp-4h]
v9 = 0x34 / a1 + 6;
v7 = 0;
v6 = a2[a1 - 1];
do
{
v7 -= 1640531527;
v10 = (v7 >> 2) & 3;
for ( i = 0; i < a1 - 1; ++i )
{
v11 = a2[i + 1];
v3 = &a2[i];
*v3 += ((v11 ^ v7) + (v6 ^ *(_DWORD *)(4LL * (v10 ^ i & 3) + a3))) ^ (((4 * v11) ^ (v6 >> 5))
+ ((v11 >> 3) ^ (16 * v6)));
v6 = *v3;
}
v4 = &a2[a1 - 1];
*v4 += ((*a2 ^ v7) + (v6 ^ *(_DWORD *)(4LL * (v10 ^ i & 3) + a3))) ^ (((4 * *a2) ^ (v6 >> 5))
+ ((*a2 >> 3) ^ (16 * v6)));
result = (unsigned int)*v4;
v6 = result;
--v9;
}
while ( v9 );
return result;
}
MD5
MD5(Message Digest Algorithm)又叫消息摘要算法,是单向散列算法(哈希算法)的一种,其对输入的任意长度的消息进行运算,产生一个 128 位的消息摘要(该过程通常不可逆)
将产生的 128 位消息摘要用十六进制表示,便是常见的 32 字符的 MD5 码
而所谓的 16 字符的 MD5 码,其实是这 32 字符中间的 16 个字符
特点
- MD5 通常会出现四个初始化常量:
0x67452301 // 1732584193
0xEFCDAB89 // -271733879
0x98BADCFE // -1732584194
0x10325476 // 271733878
在内存中小端序存放为:01 23 45 67 89 AB CD EF FE DC BA 98 76 54 32 10
- 通过 IDA 插件
Findcrypt
可以识别出 MD5
- MD5 的实现通常包含三个阶段:
MD5_Init
、MD5_Update
、MD5_Final
可能的魔改方式有:
- 改变初始化所用到的 4 个常数
- 改变填充的方法
- 改变哈希变换的处理过程
原理
这里只提一些关键特征,详细原理见如下参考文献:
- 为了后面填充 64 位的长度,需要先填充消息使其长度与
448 mod 512
同余
填充方法:附一个 1 在消息后面,然后用 0 填充,填充长度在 0 ~ 512 之间
- 最开始需要使用下面的数组进行初始化:
{ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 }
- 然后进行数据处理
需要使用左移数组,对应每轮处理的 4 * 16 = 64
步:
{ 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 }
还需要用到 64 个存放 32 位字节的加法常数数组,对应每组处理的 4 * 16 = 64
步:
{ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }
这些数值由 2 ^ 32 * (abs(sin(i)))
得出,i
的取值范围在 1 ~ 64 (这个表的存在是在 IDA 中确认 MD5 算法的关键)
接下来是 MD5 算法最核心的环节,在这里对每一组消息进行 4 轮、每轮 16 步,总计 64 步的处理
以下是每次操作中用到的四个非线性函数(每轮一个):
// 第一轮逻辑函数
F(x, y, z) = (x & y) | ((~x) & z)
// 第二轮逻辑函数
G(x, y, z) = (x & y) | (y & (~z))
// 第三轮逻辑函数
H(x, y, z) = (x ^ y ^ z)
// 第四轮逻辑函数
I(x, y, z) = x ^ (x | (~z))
定义:
FF(a, b, c, d, Mj, s, ti) 操作为 a = b + ( (a + F(b, c, d) + Mj + ti) << s)
GG(a, b, c, d, Mj, s, ti) 操作为 a = b + ( (a + G(b, c, d) + Mj + ti) << s)
HH(a, b, c, d, Mj, s, ti) 操作为 a = b + ( (a + H(b, c, d) + Mj + ti) << s)
II(a, b, c, d, Mj, s, ti) 操作为 a = b + ( (a + I(b, c, d) + Mj + ti) << s)
这里的 4 轮、每轮 16 步,总计 64 步的处理过程是:
// 第一轮
FF(a ,b ,c ,d ,M0 ,7 ,0xd76aa478 )
FF(d ,a ,b ,c ,M1 ,12 ,0xe8c7b756 )
FF(c ,d ,a ,b ,M2 ,17 ,0x242070db )
FF(b ,c ,d ,a ,M3 ,22 ,0xc1bdceee )
FF(a ,b ,c ,d ,M4 ,7 ,0xf57c0faf )
FF(d ,a ,b ,c ,M5 ,12 ,0x4787c62a )
FF(c ,d ,a ,b ,M6 ,17 ,0xa8304613 )
FF(b ,c ,d ,a ,M7 ,22 ,0xfd469501)
FF(a ,b ,c ,d ,M8 ,7 ,0x698098d8 )
FF(d ,a ,b ,c ,M9 ,12 ,0x8b44f7af )
FF(c ,d ,a ,b ,M10 ,17 ,0xffff5bb1 )
FF(b ,c ,d ,a ,M11 ,22 ,0x895cd7be )
FF(a ,b ,c ,d ,M12 ,7 ,0x6b901122 )
FF(d ,a ,b ,c ,M13 ,12 ,0xfd987193 )
FF(c ,d ,a ,b ,M14 ,17 ,0xa679438e )
FF(b ,c ,d ,a ,M15 ,22 ,0x49b40821 )
// 第二轮
GG(a ,b ,c ,d ,M1 ,5 ,0xf61e2562 )
GG(d ,a ,b ,c ,M6 ,9 ,0xc040b340 )
GG(c ,d ,a ,b ,M11 ,14 ,0x265e5a51 )
GG(b ,c ,d ,a ,M0 ,20 ,0xe9b6c7aa )
GG(a ,b ,c ,d ,M5 ,5 ,0xd62f105d )
GG(d ,a ,b ,c ,M10 ,9 ,0x02441453 )
GG(c ,d ,a ,b ,M15 ,14 ,0xd8a1e681 )
GG(b ,c ,d ,a ,M4 ,20 ,0xe7d3fbc8 )
GG(a ,b ,c ,d ,M9 ,5 ,0x21e1cde6 )
GG(d ,a ,b ,c ,M14 ,9 ,0xc33707d6 )
GG(c ,d ,a ,b ,M3 ,14 ,0xf4d50d87 )
GG(b ,c ,d ,a ,M8 ,20 ,0x455a14ed )
GG(a ,b ,c ,d ,M13 ,5 ,0xa9e3e905 )
GG(d ,a ,b ,c ,M2 ,9 ,0xfcefa3f8 )
GG(c ,d ,a ,b ,M7 ,14 ,0x676f02d9 )
GG(b ,c ,d ,a ,M12 ,20 ,0x8d2a4c8a )
// 第三轮
HH(a ,b ,c ,d ,M5 ,4 ,0xfffa3942 )
HH(d ,a ,b ,c ,M8 ,11 ,0x8771f681 )
HH(c ,d ,a ,b ,M11 ,16 ,0x6d9d6122 )
HH(b ,c ,d ,a ,M14 ,23 ,0xfde5380c )
HH(a ,b ,c ,d ,M1 ,4 ,0xa4beea44 )
HH(d ,a ,b ,c ,M4 ,11 ,0x4bdecfa9 )
HH(c ,d ,a ,b ,M7 ,16 ,0xf6bb4b60 )
HH(b ,c ,d ,a ,M10 ,23 ,0xbebfbc70 )
HH(a ,b ,c ,d ,M13 ,4 ,0x289b7ec6 )
HH(d ,a ,b ,c ,M0 ,11 ,0xeaa127fa )
HH(c ,d ,a ,b ,M3 ,16 ,0xd4ef3085 )
HH(b ,c ,d ,a ,M6 ,23 ,0x04881d05 )
HH(a ,b ,c ,d ,M9 ,4 ,0xd9d4d039 )
HH(d ,a ,b ,c ,M12 ,11 ,0xe6db99e5 )
HH(c ,d ,a ,b ,M15 ,16 ,0x1fa27cf8 )
HH(b ,c ,d ,a ,M2 ,23 ,0xc4ac5665 )
// 第四轮
II(a ,b ,c ,d ,M0 ,6 ,0xf4292244 )
II(d ,a ,b ,c ,M7 ,10 ,0x432aff97 )
II(c ,d ,a ,b ,M14 ,15 ,0xab9423a7 )
II(b ,c ,d ,a ,M5 ,21 ,0xfc93a039 )
II(a ,b ,c ,d ,M12 ,6 ,0x655b59c3 )
II(d ,a ,b ,c ,M3 ,10 ,0x8f0ccc92 )
II(c ,d ,a ,b ,M10 ,15 ,0xffeff47d )
II(b ,c ,d ,a ,M1 ,21 ,0x85845dd1 )
II(a ,b ,c ,d ,M8 ,6 ,0x6fa87e4f )
II(d ,a ,b ,c ,M15 ,10 ,0xfe2ce6e0 )
II(c ,d ,a ,b ,M6 ,15 ,0xa3014314 )
II(b ,c ,d ,a ,M13 ,21 ,0x4e0811a1 )
II(a ,b ,c ,d ,M4 ,6 ,0xf7537e82 )
II(d ,a ,b ,c ,M11 ,10 ,0xbd3af235 )
II(c ,d ,a ,b ,M2 ,15 ,0x2ad7d2bb )
II(b ,c ,d ,a ,M9 ,21 ,0xeb86d391 )
- 如果 128 位消息已经处理完,则从低地址开始用 16 进制逐个输出字节便得到 32 字符的 MD5 码,取正中间的 16 字符就是 16 字符的 MD5 码
加密代码
Python 版
import hashlib
string = 'flag{welcome_to_uf4te!}'
def md5value(key):
input_name = hashlib.md5()
input_name.update(key.encode("utf-8"))
print("大写的32位 : " + (input_name.hexdigest()).upper())
print("大写的16位 : " + (input_name.hexdigest())[8:-8].upper())
print("小写的32位 : " + (input_name.hexdigest()).lower())
print("小写的16位 : " + (input_name.hexdigest())[8:-8].lower())
md5value(string)
''' 输出:
大写的 32 位 MD5 : B2E8ED4649266019056425F0E67A62A7
大写的 16 位 MD5 : 49266019056425F0
小写的 32 位 MD5 : b2e8ed4649266019056425f0e67a62a7
小写的 16 位 MD5 : 49266019056425f0
'''
IDA 示例
// MD5_Init
_DWORD *__cdecl sub_4012B0(_DWORD *a1)
{
_DWORD *result; // eax
result = a1;
a1[5] = 0;
a1[4] = 0;
*a1 = 1732584193;
a1[1] = -271733879;
a1[2] = -1732584194;
a1[3] = 271733878;
return result;
}
// MD5_Update
int __cdecl sub_4012E0(int a1, int a2, unsigned int a3)
{
unsigned int v3; // ecx
int v4; // eax
int v5; // ebx
int v6; // ebp
unsigned int i; // ebx
v3 = *(a1 + 16) + 8 * a3;
v4 = (*(a1 + 16) >> 3) & 0x3F;
*(a1 + 16) = v3;
if ( v3 < 8 * a3 )
++*(a1 + 20);
*(a1 + 20) += a3 >> 29;
v5 = 64 - v4;
if ( a3 < 64 - v4 )
{
v6 = 0;
}
else
{
sub_401DF0(v4 + a1 + 24, a2, 64 - v4);
sub_401400(a1, a1 + 24);
v6 = v5;
for ( i = v5 + 63; i < a3; v6 += 64 )
{
sub_401400(a1, a2 + i - 63);
i += 64;
}
v4 = 0;
}
return sub_401DF0(v4 + a1 + 24, a2 + v6, a3 - v6);
}
int __cdecl sub_401400(int *a1, int a2)
{
int v3; // edi
int v4; // ebx
int v5; // ebp
unsigned __int64 v6; // kr00_8
int v7; // eax
unsigned __int64 v8; // kr08_8
int v9; // ecx
unsigned int v10; // ebx
int v11; // edx
int v12; // edi
int v13; // edi
unsigned int v14; // ecx
int v15; // eax
unsigned int v16; // edx
int v17; // ecx
unsigned int v18; // edi
int v19; // edx
unsigned int v20; // eax
int v21; // edi
unsigned int v22; // ecx
int v23; // eax
unsigned int v24; // edx
int v25; // ecx
unsigned int v26; // edi
int v27; // edx
unsigned int v28; // eax
int v29; // edi
unsigned int v30; // ecx
int v31; // eax
unsigned int v32; // edx
int v33; // ecx
unsigned int v34; // edi
int v35; // edx
unsigned int v36; // eax
int v37; // edi
unsigned int v38; // ecx
int v39; // eax
unsigned int v40; // edx
int v41; // ecx
unsigned int v42; // edi
int v43; // edx
unsigned int v44; // eax
int v45; // edi
unsigned int v46; // ecx
int v47; // eax
unsigned int v48; // edx
int v49; // ecx
unsigned int v50; // edi
int v51; // edx
unsigned int v52; // eax
unsigned int v53; // edi
unsigned int v54; // eax
unsigned int v55; // ecx
unsigned int v56; // ebx
unsigned int v57; // edx
unsigned int v58; // edi
unsigned int v59; // eax
unsigned int v60; // ecx
unsigned int v61; // ebx
unsigned int v62; // eax
unsigned int v63; // edx
unsigned int v64; // ebx
unsigned int v65; // edi
unsigned int v66; // edx
unsigned int v67; // ecx
unsigned int v68; // eax
unsigned int v69; // ebx
unsigned int v70; // edi
unsigned int v71; // edx
unsigned int v72; // ebx
unsigned int v73; // ecx
unsigned int v74; // eax
unsigned int v75; // edi
unsigned int v76; // edx
unsigned int v77; // ebx
unsigned int v78; // edi
unsigned int v79; // ecx
unsigned int v80; // ebx
unsigned int v81; // eax
unsigned int v82; // ecx
unsigned int v83; // edx
int v84; // eax
unsigned int v85; // edi
int v86; // edx
unsigned int v87; // ebx
int v88; // edi
unsigned int v89; // ecx
int v90; // ebx
unsigned int v91; // eax
int v92; // ecx
unsigned int v93; // edx
int v94; // eax
unsigned int v95; // edi
int v96; // edx
unsigned int v97; // ebx
int v98; // edi
unsigned int v99; // ecx
int v100; // ebx
unsigned int v101; // eax
int v102; // ecx
unsigned int v103; // edx
int v104; // eax
unsigned int v105; // edi
int v106; // edx
unsigned int v107; // ebx
int v108; // edi
unsigned int v109; // ecx
int v110; // ebx
int v111; // ebp
unsigned int v112; // eax
int v113; // ecx
int v114; // edx
int v115; // ebx
int v117; // [esp+10h] [ebp-40h] BYREF
int v118; // [esp+14h] [ebp-3Ch]
int v119; // [esp+18h] [ebp-38h]
int v120; // [esp+1Ch] [ebp-34h]
int v121; // [esp+20h] [ebp-30h]
int v122; // [esp+24h] [ebp-2Ch]
int v123; // [esp+28h] [ebp-28h]
int v124; // [esp+2Ch] [ebp-24h]
int v125; // [esp+30h] [ebp-20h]
int v126; // [esp+34h] [ebp-1Ch]
int v127; // [esp+38h] [ebp-18h]
int v128; // [esp+3Ch] [ebp-14h]
int v129; // [esp+40h] [ebp-10h]
int v130; // [esp+44h] [ebp-Ch]
int v131; // [esp+48h] [ebp-8h]
int v132; // [esp+4Ch] [ebp-4h]
int v133; // [esp+54h] [ebp+4h]
int v134; // [esp+54h] [ebp+4h]
int v135; // [esp+54h] [ebp+4h]
int v136; // [esp+54h] [ebp+4h]
int v137; // [esp+54h] [ebp+4h]
int v138; // [esp+54h] [ebp+4h]
int v139; // [esp+54h] [ebp+4h]
int v140; // [esp+54h] [ebp+4h]
v3 = a1[1];
v4 = a1[2];
v5 = a1[3];
v133 = *a1;
sub_401DA0(&v117, a2, 64);
v6 = (v133 + v117 + (v3 & v4 | v5 & ~v3) - 680876936) << 7;
v7 = v3 + (v6 | HIDWORD(v6));
v8 = (v118 + (v7 & v3 | v4 & ~v7) + v5 - 389564586) << 12;
v9 = v7 + (v8 | HIDWORD(v8));
v10 = v4 + v119 + (v7 & v9 | v3 & ~v9) + 606105819;
v11 = v9 + ((v10 << 17) | (v10 >> 15));
v134 = v11
+ (((v3 + v120 + (v11 & v9 | v7 & ~v11) - 1044525330) >> 10) | ((v3 + v120 + (v11 & v9 | v7 & ~v11) - 1044525330) << 22));
v12 = v121 + (v134 & v11 | v9 & ~v134);
v13 = v134 + (((v7 + v12 - 176418897) << 7) | ((v7 + v12 - 176418897) >> 25));
v14 = v9 + v122 + (v13 & v134 | v11 & ~v13) + 1200080426;
v15 = v13 + ((v14 << 12) | (v14 >> 20));
v16 = v11 + v123 + (v13 & v15 | v134 & ~v15) - 1473231341;
v17 = v15 + ((v16 << 17) | (v16 >> 15));
v135 = v17
+ (((v134 + v124 + (v17 & v15 | v13 & ~v17) - 45705983) >> 10) | ((v134
+ v124
+ (v17 & v15 | v13 & ~v17)
- 45705983) << 22));
v18 = v13 + v125 + (v135 & v17 | v15 & ~v135) + 1770035416;
v19 = v135 + ((v18 << 7) | (v18 >> 25));
v20 = v15 + v126 + (v19 & v135 | v17 & ~v19) - 1958414417;
v21 = v19 + ((v20 << 12) | (v20 >> 20));
v22 = v17 + v127 + (v19 & v21 | v135 & ~v21) - 42063;
v23 = v21 + ((v22 << 17) | (v22 >> 15));
v136 = v23
+ (((v135 + v128 + (v23 & v21 | v19 & ~v23) - 1990404162) >> 10) | ((v135
+ v128
+ (v23 & v21 | v19 & ~v23)
- 1990404162) << 22));
v24 = v19 + v129 + (v136 & v23 | v21 & ~v136) + 1804603682;
v25 = v136 + ((v24 << 7) | (v24 >> 25));
v26 = v21 + v130 + (v25 & v136 | v23 & ~v25) - 40341101;
v27 = v25 + ((v26 << 12) | (v26 >> 20));
v28 = v23 + v131 + (~v27 & v136 | v25 & v27) - 1502002290;
v29 = v27 + ((v28 << 17) | (v28 >> 15));
v137 = v29
+ (((v136 + v132 + (v29 & v27 | v25 & ~v29) + 1236535329) >> 10) | ((v136
+ v132
+ (v29 & v27 | v25 & ~v29)
+ 1236535329) << 22));
v30 = v25 + v118 + (~v27 & v29 | v137 & v27) - 165796510;
v31 = v137 + ((32 * v30) | (v30 >> 27));
v32 = v27 + v123 + (v31 & v29 | v137 & ~v29) - 1069501632;
v33 = v31 + ((v32 << 9) | (v32 >> 23));
v34 = v29 + v128 + (v137 & v33 | v31 & ~v137) + 643717713;
v35 = v33 + ((v34 << 14) | (v34 >> 18));
v138 = v35
+ (((v137 + v117 + (v31 & v35 | v33 & ~v31) - 373897302) >> 12) | ((v137
+ v117
+ (v31 & v35 | v33 & ~v31)
- 373897302) << 20));
v36 = v31 + v122 + (v138 & v33 | v35 & ~v33) - 701558691;
v37 = v138 + ((32 * v36) | (v36 >> 27));
v38 = v33 + v127 + (v37 & v35 | v138 & ~v35) + 38016083;
v39 = v37 + ((v38 << 9) | (v38 >> 23));
v40 = v35 + v132 + (v138 & v39 | v37 & ~v138) - 660478335;
v41 = v39 + ((v40 << 14) | (v40 >> 18));
v139 = v41
+ (((v138 + v121 + (v37 & v41 | v39 & ~v37) - 405537848) >> 12) | ((v138
+ v121
+ (v37 & v41 | v39 & ~v37)
- 405537848) << 20));
v42 = v37 + v126 + (v139 & v39 | v41 & ~v39) + 568446438;
v43 = v139 + ((32 * v42) | (v42 >> 27));
v44 = v39 + v131 + (v43 & v41 | v139 & ~v41) - 1019803690;
v45 = v43 + ((v44 << 9) | (v44 >> 23));
v46 = v41 + v120 + (v139 & v45 | v43 & ~v139) - 187363961;
v47 = v45 + ((v46 << 14) | (v46 >> 18));
v140 = v47
+ (((v139 + v125 + (v43 & v47 | v45 & ~v43) + 1163531501) >> 12) | ((v139
+ v125
+ (v43 & v47 | v45 & ~v43)
+ 1163531501) << 20));
v48 = v43 + v130 + (v140 & v45 | v47 & ~v45) - 1444681467;
v49 = v140 + ((32 * v48) | (v48 >> 27));
v50 = v45 + v119 + (v49 & v47 | v140 & ~v47) - 51403784;
v51 = v49 + ((v50 << 9) | (v50 >> 23));
v52 = v47 + v124 + (v140 & v51 | v49 & ~v140) + 1735328473;
v53 = v51 + ((v52 << 14) | (v52 >> 18));
v54 = v53
+ (((v140 + v129 + (v49 & v53 | v51 & ~v49) - 1926607734) >> 12) | ((v140
+ v129
+ (v49 & v53 | v51 & ~v49)
- 1926607734) << 20));
v55 = v49 + v122 + (v54 ^ v53 ^ v51) - 378558;
v56 = v54 + ((16 * v55) | (v55 >> 28));
v57 = v56
+ (((v51 + v125 + (v56 ^ v54 ^ v53) - 2022574463) << 11) | ((v51 + v125 + (v56 ^ v54 ^ v53) - 2022574463) >> 21));
v58 = v57
+ (((v53 + v128 + (v56 ^ v54 ^ v57) + 1839030562) << 16) | ((v53 + v128 + (v56 ^ v54 ^ v57) + 1839030562) >> 16));
v59 = v54 + v131 + (v56 ^ v58 ^ v57) - 35309556;
v60 = v58 + ((v59 >> 9) | (v59 << 23));
v61 = v56 + v118 + (v60 ^ v58 ^ v57) - 1530992060;
v62 = v60 + ((16 * v61) | (v61 >> 28));
v63 = v57 + v121 + (v62 ^ v60 ^ v58) + 1272893353;
v64 = v62 + ((v63 << 11) | (v63 >> 21));
v65 = v58 + v124 + (v62 ^ v60 ^ v64) - 155497632;
v66 = v64 + ((v65 << 16) | HIWORD(v65));
v67 = v66
+ (((v60 + v127 + (v62 ^ v66 ^ v64) - 1094730640) >> 9) | ((v60 + v127 + (v62 ^ v66 ^ v64) - 1094730640) << 23));
v68 = v67
+ ((16 * (v62 + v130 + (v67 ^ v66 ^ v64) + 681279174)) | ((v62 + v130 + (v67 ^ v66 ^ v64) + 681279174) >> 28));
v69 = v64 + v117 + (v68 ^ v67 ^ v66) - 358537222;
v70 = v68 + ((v69 << 11) | (v69 >> 21));
v71 = v66 + v120 + (v68 ^ v67 ^ v70) - 722521979;
v72 = v70 + ((v71 << 16) | HIWORD(v71));
v73 = v72 + (((v67 + v123 + (v68 ^ v72 ^ v70) + 76029189) >> 9) | ((v67 + v123 + (v68 ^ v72 ^ v70) + 76029189) << 23));
v74 = v73
+ ((16 * (v68 + v126 + (v73 ^ v72 ^ v70) - 640364487)) | ((v68 + v126 + (v73 ^ v72 ^ v70) - 640364487) >> 28));
v75 = v70 + v129 + (v74 ^ v73 ^ v72) - 421815835;
v76 = v74 + ((v75 << 11) | (v75 >> 21));
v77 = v72 + v132 + (v74 ^ v73 ^ v76) + 530742520;
v78 = v76 + ((v77 << 16) | HIWORD(v77));
v79 = v73 + v119 + (v74 ^ v78 ^ v76) - 995338651;
v80 = v78 + ((v79 >> 9) | (v79 << 23));
v81 = v74 + v117 + (v78 ^ (v80 | ~v76)) - 198630844;
v82 = v80 + ((v81 << 6) | (v81 >> 26));
v83 = v76 + v124 + (v80 ^ (v82 | ~v78)) + 1126891415;
v84 = v82 + ((v83 << 10) | (v83 >> 22));
v85 = v78 + v131 + (v82 ^ (v84 | ~v80)) - 1416354905;
v86 = v84 + ((v85 << 15) | (v85 >> 17));
v87 = v80 + v122 + (v84 ^ (v86 | ~v82)) - 57434055;
v88 = v86 + ((v87 >> 11) | (v87 << 21));
v89 = v82 + v129 + (v86 ^ (v88 | ~v84)) + 1700485571;
v90 = v88 + ((v89 << 6) | (v89 >> 26));
v91 = v84 + v120 + (v88 ^ (v90 | ~v86)) - 1894986606;
v92 = v90 + ((v91 << 10) | (v91 >> 22));
v93 = v86 + v127 + (v90 ^ (v92 | ~v88)) - 1051523;
v94 = v92 + ((v93 << 15) | (v93 >> 17));
v95 = v88 + v118 + (v92 ^ (v94 | ~v90)) - 2054922799;
v96 = v94 + ((v95 >> 11) | (v95 << 21));
v97 = v90 + v125 + (v94 ^ (v96 | ~v92)) + 1873313359;
v98 = v96 + ((v97 << 6) | (v97 >> 26));
v99 = v92 + v132 + (v96 ^ (v98 | ~v94)) - 30611744;
v100 = v98 + ((v99 << 10) | (v99 >> 22));
v101 = v94 + v123 + (v98 ^ (v100 | ~v96)) - 1560198380;
v102 = v100 + ((v101 << 15) | (v101 >> 17));
v103 = v96 + v130 + (v100 ^ (v102 | ~v98)) + 1309151649;
v104 = v102 + ((v103 >> 11) | (v103 << 21));
v105 = v98 + v121 + (v102 ^ (v104 | ~v100)) - 145523070;
v106 = v104 + ((v105 << 6) | (v105 >> 26));
v107 = v100 + v128 + (v104 ^ (v106 | ~v102)) - 1120210379;
v108 = v106 + ((v107 << 10) | (v107 >> 22));
v109 = v102 + v119 + (v106 ^ (v108 | ~v104)) + 718787259;
v110 = v108 + ((v109 << 15) | (v109 >> 17));
v111 = v110 + a1[2];
v112 = v104 + v126 + (v108 ^ (v110 | ~v106)) - 343485551;
v113 = v106 + *a1;
v114 = v110 + ((v112 >> 11) | (v112 << 21));
v115 = v108 + a1[3];
a1[1] += v114;
*a1 = v113;
a1[2] = v111;
a1[3] = v115;
return sub_401E10(&v117, 0, 64);
}
// MD5_Final
int __cdecl sub_401390(int a1, int a2)
{
unsigned int v2; // eax
int v3; // ecx
char v5[8]; // [esp+8h] [ebp-8h] BYREF
sub_401D50(v5, a2 + 16, 8);
v2 = (*(a2 + 16) >> 3) & 0x3F;
v3 = 56;
if ( v2 >= 0x38 )
v3 = 120;
sub_4012E0(a2, &unk_407030, v3 - v2);
sub_4012E0(a2, v5, 8u);
sub_401D50(a1, a2, 16);
return sub_401E10(a2, 0, 88);
}
int __cdecl sub_4012E0(int a1, int a2, unsigned int a3)
{
unsigned int v3; // ecx
int v4; // eax
int v5; // ebx
int v6; // ebp
unsigned int i; // ebx
v3 = *(a1 + 16) + 8 * a3;
v4 = (*(a1 + 16) >> 3) & 0x3F;
*(a1 + 16) = v3;
if ( v3 < 8 * a3 )
++*(a1 + 20);
*(a1 + 20) += a3 >> 29;
v5 = 64 - v4;
if ( a3 < 64 - v4 )
{
v6 = 0;
}
else
{
sub_401DF0(v4 + a1 + 24, a2, 64 - v4);
sub_401400(a1, a1 + 24);
v6 = v5;
for ( i = v5 + 63; i < a3; v6 += 64 )
{
sub_401400(a1, a2 + i - 63);
i += 64;
}
v4 = 0;
}
return sub_401DF0(v4 + a1 + 24, a2 + v6, a3 - v6);
}
DES
DES 加密,即即数据加密标准(Data Encryption Standard),属于对称加密算法,是一种使用密钥加密的分组算法
参考文献:
特点
Key 为 8 字节共 64 位(实际使用 56 位),明文或密文也为 8 字节 64 位
DES 算法一般有两个关键点:加密算法和数据补位
存在 8 个置换盒
S1 ~ S8
int S1[] = {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13};
int S2[] = {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9};
int S3[] = {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12};
int S4[] = { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14};
int S5[] = { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3};
int S6[] = {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13};
int S7[] = { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12};
int S8[] = {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11};
原理
加解密代码
Python 版(CBC 模式)
from pyDes import des, CBC, PAD_PKCS5
import binascii
key = 'hi_uf4te' # 秘钥, 8 位
iv = 'uf4te!!!' # 偏移量 iv, 8 位
def des_encrypt(s):
# key : 加密密钥, CBC : 加密模式,iv : 偏移, padmode : 填充
des_obj = des(key, CBC, iv, pad=None, padmode=PAD_PKCS5)
# 返回为字节
secret_bytes = des_obj.encrypt(s, padmode=PAD_PKCS5)
# 将字节对象转换为十六进制字符串
return binascii.b2a_hex(secret_bytes)
def des_descrypt(s):
# key : 加密密钥, CBC : 加密模式,iv : 偏移, padmode : 填充
des_obj = des(key, CBC, iv, pad=None, padmode=PAD_PKCS5)
decrypt_str = des_obj.decrypt(binascii.a2b_hex(s), padmode=PAD_PKCS5)
return decrypt_str
text = b'welcome_to_uf4te' # 加解密内容
enc = des_encrypt(text) # DES 加密,结果为 16 进制字节流
print(enc)
dec = des_descrypt(enc) # DES 解密
print(dec)
''' 输出:
b'54576e252cc9973824e53ea43eb28489b3bb662a97e07165'
b'welcome_to_uf4te'
'''
Python 版(ECB 模式)
from Crypto.Cipher import DES
import binascii
key = b'hi_uf4te' # 密钥 8 位或 16 位,必须为 bytes 类型
text = 'welcome_to_uf4te'
def pad(text):
# 如果 text 不是 8 的倍数【加密文本 text 必须为 8 的倍数!】,补足为 8 的倍数
while len(text) % 8 != 0:
text += ' '
return text
des = DES.new(key, DES.MODE_ECB) # 创建 DES 实例
padded_text = pad(text) # 填充
enc = des.encrypt(padded_text.encode('utf-8')) # 加密,结果为 16 进制字节流
print(binascii.b2a_hex(enc)) # 将字节对象转换为十六进制字符串
dec = des.decrypt(enc).decode().rstrip(' ') # 解密
print(dec)
''' 输出:
b'8c8641d37affbc2b251cbd968cb124a8'
welcome_to_uf4te
'''
AES
AES 加密,即高级加密标准(Advanced Encryption Standard),属于对称加密算法,基于数据块的加密方式:分组输入、分组输出,该算法已被用来替代 DES 算法,并在世界范围内广泛使用
AES 加密算法的速度比公钥加密等加密算法快很多,在很多场合都需要 AES 对称加密,主要缺点在于要求加密和解密双方都使用相同的密钥
参考文献:
特点
- AES 按照密钥的长度可以分为:AES-128、AES-192、AES-256 三种
密钥长度 | 分组长度 | 轮数 | |
---|---|---|---|
AES-128 | 128 bit | 4 bit | 10 轮 |
AES-192 | 192 bit | 4 bit | 12 轮 |
AES-256 | 256 bit | 4 bit | 14 轮 |
- 加解密的字节替代过程中使用了
s_box
和逆s_box
完成一个字节到另一个字节的映射
s
盒:
行/列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0x63 | 0x7c | 0x77 | 0x7b | 0xf2 | 0x6b | 0x6f | 0xc5 | 0x30 | 0x01 | 0x67 | 0x2b | 0xfe | 0xd7 | 0xab | 0x76 |
1 | 0xca | 0x82 | 0xc9 | 0x7d | 0xfa | 0x59 | 0x47 | 0xf0 | 0xad | 0xd4 | 0xa2 | 0xaf | 0x9c | 0xa4 | 0x72 | 0xc0 |
2 | 0xb7 | 0xfd | 0x93 | 0x26 | 0x36 | 0x3f | 0xf7 | 0xcc | 0x34 | 0xa5 | 0xe5 | 0xf1 | 0x71 | 0xd8 | 0x31 | 0x15 |
3 | 0x04 | 0xc7 | 0x23 | 0xc3 | 0x18 | 0x96 | 0x05 | 0x9a | 0x07 | 0x12 | 0x80 | 0xe2 | 0xeb | 0x27 | 0xb2 | 0x75 |
4 | 0x09 | 0x83 | 0x2c | 0x1a | 0x1b | 0x6e | 0x5a | 0xa0 | 0x52 | 0x3b | 0xd6 | 0xb3 | 0x29 | 0xe3 | 0x2f | 0x84 |
5 | 0x53 | 0xd1 | 0x00 | 0xed | 0x20 | 0xfc | 0xb1 | 0x5b | 0x6a | 0xcb | 0xbe | 0x39 | 0x4a | 0x4c | 0x58 | 0xcf |
6 | 0xd0 | 0xef | 0xaa | 0xfb | 0x43 | 0x4d | 0x33 | 0x85 | 0x45 | 0xf9 | 0x02 | 0x7f | 0x50 | 0x3c | 0x9f | 0xa8 |
7 | 0x51 | 0xa3 | 0x40 | 0x8f | 0x92 | 0x9d | 0x38 | 0xf5 | 0xbc | 0xb6 | 0xda | 0x21 | 0x10 | 0xff | 0xf3 | 0xd2 |
8 | 0xcd | 0x0c | 0x13 | 0xec | 0x5f | 0x97 | 0x44 | 0x17 | 0xc4 | 0xa7 | 0x7e | 0x3d | 0x64 | 0x5d | 0x19 | 0x73 |
9 | 0x60 | 0x81 | 0x4f | 0xdc | 0x22 | 0x2a | 0x90 | 0x88 | 0x46 | 0xee | 0xb8 | 0x14 | 0xde | 0x5e | 0x0b | 0xdb |
A | 0xe0 | 0x32 | 0x3a | 0x0a | 0x49 | 0x06 | 0x24 | 0x5c | 0xc2 | 0xd3 | 0xac | 0x62 | 0x91 | 0x95 | 0xe4 | 0x79 |
B | 0xe7 | 0xc8 | 0x37 | 0x6d | 0x8d | 0xd5 | 0x4e | 0xa9 | 0x6c | 0x56 | 0xf4 | 0xea | 0x65 | 0x7a | 0xae | 0x08 |
C | 0xba | 0x78 | 0x25 | 0x2e | 0x1c | 0xa6 | 0xb4 | 0xc6 | 0xe8 | 0xdd | 0x74 | 0x1f | 0x4b | 0xbd | 0x8b | 0x8a |
D | 0x70 | 0x3e | 0xb5 | 0x66 | 0x48 | 0x03 | 0xf6 | 0x0e | 0x61 | 0x35 | 0x57 | 0xb9 | 0x86 | 0xc1 | 0x1d | 0x9e |
E | 0xe1 | 0xf8 | 0x98 | 0x11 | 0x69 | 0xd9 | 0x8e | 0x94 | 0x9b | 0x1e | 0x87 | 0xe9 | 0xce | 0x55 | 0x28 | 0xdf |
F | 0x8c | 0xa1 | 0x89 | 0x0d | 0xbf | 0xe6 | 0x42 | 0x68 | 0x41 | 0x99 | 0x2d | 0x0f | 0xb0 | 0x54 | 0xbb | 0x16 |
逆 s
盒:
行/列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0x52 | 0x09 | 0x6a | 0xd5 | 0x30 | 0x36 | 0xa5 | 0x38 | 0xbf | 0x40 | 0xa3 | 0x9e | 0x81 | 0xf3 | 0xd7 | 0xfb |
1 | 0x7c | 0xe3 | 0x39 | 0x82 | 0x9b | 0x2f | 0xff | 0x87 | 0x34 | 0x8e | 0x43 | 0x44 | 0xc4 | 0xde | 0xe9 | 0xcb |
2 | 0x54 | 0x7b | 0x94 | 0x32 | 0xa6 | 0xc2 | 0x23 | 0x3d | 0xee | 0x4c | 0x95 | 0x0b | 0x42 | 0xfa | 0xc3 | 0x4e |
3 | 0x08 | 0x2e | 0xa1 | 0x66 | 0x28 | 0xd9 | 0x24 | 0xb2 | 0x76 | 0x5b | 0xa2 | 0x49 | 0x6d | 0x8b | 0xd1 | 0x25 |
4 | 0x72 | 0xf8 | 0xf6 | 0x64 | 0x86 | 0x68 | 0x98 | 0x16 | 0xd4 | 0xa4 | 0x5c | 0xcc | 0x5d | 0x65 | 0xb6 | 0x92 |
5 | 0x6c | 0x70 | 0x48 | 0x50 | 0xfd | 0xed | 0xb9 | 0xda | 0x5e | 0x15 | 0x46 | 0x57 | 0xa7 | 0x8d | 0x9d | 0x84 |
6 | 0x90 | 0xd8 | 0xab | 0x00 | 0x8c | 0xbc | 0xd3 | 0x0a | 0xf7 | 0xe4 | 0x58 | 0x05 | 0xb8 | 0xb3 | 0x45 | 0x06 |
7 | 0xd0 | 0x2c | 0x1e | 0x8f | 0xca | 0x3f | 0x0f | 0x02 | 0xc1 | 0xaf | 0xbd | 0x03 | 0x01 | 0x13 | 0x8a | 0x6b |
8 | 0x3a | 0x91 | 0x11 | 0x41 | 0x4f | 0x67 | 0xdc | 0xea | 0x97 | 0xf2 | 0xcf | 0xce | 0xf0 | 0xb4 | 0xe6 | 0x73 |
9 | 0x96 | 0xac | 0x74 | 0x22 | 0xe7 | 0xad | 0x35 | 0x85 | 0xe2 | 0xf9 | 0x37 | 0xe8 | 0x1c | 0x75 | 0xdf | 0x6e |
A | 0x47 | 0xf1 | 0x1a | 0x71 | 0x1d | 0x29 | 0xc5 | 0x89 | 0x6f | 0xb7 | 0x62 | 0x0e | 0xaa | 0x18 | 0xbe | 0x1b |
B | 0xfc | 0x56 | 0x3e | 0x4b | 0xc6 | 0xd2 | 0x79 | 0x20 | 0x9a | 0xdb | 0xc0 | 0xfe | 0x78 | 0xcd | 0x5a | 0xf4 |
C | 0x1f | 0xdd | 0xa8 | 0x33 | 0x88 | 0x07 | 0xc7 | 0x31 | 0xb1 | 0x12 | 0x10 | 0x59 | 0x27 | 0x80 | 0xec | 0x5f |
D | 0x60 | 0x51 | 0x7f | 0xa9 | 0x19 | 0xb5 | 0x4a | 0x0d | 0x2d | 0xe5 | 0x7a | 0x9f | 0x93 | 0xc9 | 0x9c | 0xef |
E | 0xa0 | 0xe0 | 0x3b | 0x4d | 0xae | 0x2a | 0xf5 | 0xb0 | 0xc8 | 0xeb | 0xbb | 0x3c | 0x83 | 0x53 | 0x99 | 0x61 |
F | 0x17 | 0x2b | 0x04 | 0x7e | 0xba | 0x77 | 0xd6 | 0x26 | 0xe1 | 0x69 | 0x14 | 0x63 | 0x55 | 0x21 | 0x0c | 0x7d |
- 通过 IDA 插件
Findcrypt
可以识别出 AES
原理
详细过程参考:
AES 常用的加密工作模式有:ECB,CBC,OFB,CFB,CTR
模式 | 含义 | 偏移量 IV |
---|---|---|
ECB | 电子密码本模式 | 无 |
CBC | 密码分组链接模式 | 有 |
OFB | 输出反馈模式 | 有 |
CFB | 密码反馈模式 | 有 |
CTR | 计数器模式 | 有 |
最常用的是 ECB 和 CBC 模式,主要区别在于 ECB 无需偏移量 IV,CBC 需要偏移量
加解密代码
如果在 Windows 平台下,使用
from Crypto.Cipher import AES
遇到关于Module Not Found Error : No module named 'Crypto'
的报错,可尝试如下办法解决参考文献:
pip uninstall crypto
pip uninstall pycryptodome
pip install pycryptodome
Python 版(CBC 模式)
from Crypto.Cipher import AES
import binascii
key = b'1234567812345678' # 秘钥,bytes 类型
iv = b'1234567812345678' # iv 偏移量,bytes 类型
text = b'welcome_to_uf4te' # 需要加解密的内容,bytes 类型
aes = AES.new(key, AES.MODE_CBC, iv) # 创建一个 AES 对象,AES.MODE_CBC 表示 CBC 模式
den_text = aes.decrypt(text) # 解密,bytes 类型
print("解密:", binascii.b2a_hex(den_text)) # 将字节对象转换为十六进制字符串
aes = AES.new(key, AES.MODE_CBC, iv) # CBC 模式下解密需要重新创建一个 AES 对象,AES.MODE_CBC 表示 CBC 模式
en_text = aes.encrypt(den_text) # 加密,bytes 类型
print("加密:", en_text)
''' 输出:
解密: b'576fab9d1e22110b39027d9d85f7417f'
加密: b'welcome_to_uf4te'
'''
Python 版(ECB 模式)
from Crypto.Cipher import AES
import binascii
key = b'1234567812345678' # 秘钥,bytes 类型
text = b'weclome_to_uf4te' # 需要加解密的内容,bytes 类型
aes = AES.new(key, AES.MODE_ECB) # 创建一个 AES 对象,AES.MODE_ECB 表示模式是 ECB 模式
den_text = aes.decrypt(text) # 解密,bytes 类型
print("解密:", binascii.b2a_hex(den_text))
en_text = aes.encrypt(den_text) # 加密,bytes 类型
print("加密:", en_text)
''' 输出:
解密: b'914532f3c0677e44c5169a096ffccbd7'
加密: b'weclome_to_uf4te'
'''
IDA 示例
int __cdecl sub_401EC0(int *a1, int a2, int a3, int a4, int a5)
{
int v5; // ebx
int *v7; // ebp
int v8; // eax
char *v10; // esi
int v11; // esi
int *v12; // edi
char *v13; // esi
int v14; // eax
int v15; // ecx
int *v16; // eax
char *v17; // edx
_DWORD *v18; // ebp
int v19; // edx
int v20; // ecx
int *v21; // eax
char *v22; // edx
bool v23; // cc
int *v24; // edx
int *v25; // ecx
int v26; // esi
int v27; // edi
int v28; // edi
_DWORD *v29; // esi
_DWORD *v30; // edi
int v31; // ebx
bool v32; // zf
int v33; // ebx
int v34; // ecx
int *v35; // edx
int v36; // esi
int v37; // edi
int v38; // eax
char v39[32]; // [esp+8h] [ebp-20h] BYREF
int v40; // [esp+2Ch] [ebp+4h]
_DWORD *v41; // [esp+30h] [ebp+8h]
int v42; // [esp+30h] [ebp+8h]
int v43; // [esp+34h] [ebp+Ch]
char *v44; // [esp+38h] [ebp+10h]
int v45; // [esp+3Ch] [ebp+14h]
int v46; // [esp+3Ch] [ebp+14h]
unsigned int v47; // [esp+3Ch] [ebp+14h]
v5 = a3 / 4;
if ( a3 / 4 != 4 && v5 != 6 && v5 != 8 )
return 0;
v7 = a1;
*a1 = v5;
a1[1] = v5 + 6;
sub_401E80(a1, a2, a5);
v8 = 4 * (v5 + 6) + 4;
v43 = v8;
if ( v5 > 0 )
{
v10 = v39;
v45 = v5;
do
{
*v10 = sub_4021A0(a4);
v10 += 4;
a4 += 4;
--v45;
}
while ( v45 );
v8 = 4 * (v5 + 6) + 4;
qmemcpy(a1 + 3, v39, 4 * v5);
}
v46 = v5;
if ( v5 < v8 )
{
v41 = &unk_4084DC;
v11 = -8 - a1;
v12 = &a1[v5 + 2];
while ( 1 )
{
v13 = v12 + v11;
v14 = sub_4021D0((*v12 >> 8) | (*v12 << 24)); // sub_4021D0 函数对 s_box 进行字节的映射
v15 = 1;
v12[1] = *v41 ^ *&v13[a1 + 12 + -4 * v5] ^ v14;
if ( v5 > 6 )
{
v18 = v12 + 2;
v44 = &v13[a1 + 16 + -4 * v5];
do
{
if ( v15 + v46 >= v43 )
break;
v19 = *(v18++ - 1);
++v15;
*(v18 - 1) = *v44 ^ v19;
v44 += 4;
}
while ( v15 < 4 );
if ( v46 + 4 < v43 )
v12[5] = *&v13[a1 + 28 + -4 * v5] ^ sub_4021D0(v12[4]);
v20 = 5;
v21 = v12 + 6;
v22 = &v13[a1 + 32 + -4 * v5];
do
{
if ( v20 + v46 >= v43 )
break;
++v20;
*v21 = *v22 ^ *(v21 - 1);
v22 += 4;
++v21;
}
while ( v20 < v5 );
}
else if ( v5 > 1 )
{
v16 = v12 + 2;
v17 = &v13[a1 + 16 + -4 * v5];
do
{
if ( v15 + v46 >= v43 )
break;
++v15;
*v16 = *v17 ^ *(v16 - 1);
v17 += 4;
++v16;
}
while ( v15 < v5 );
}
v12 += v5;
v8 = 4 * (v5 + 6) + 4;
v23 = v5 + v46 < v43;
v46 += v5;
++v41;
if ( !v23 )
break;
v11 = -8 - a1;
}
v7 = a1;
}
v24 = &v7[v8 + 59];
v25 = v7 + 3;
v26 = 4;
do
{
v27 = *v25++;
*v24++ = v27;
--v26;
}
while ( v26 );
v28 = v8 - 4;
v42 = v8 - 4;
if ( v8 - 4 > 4 )
{
v29 = v7 + 7;
v40 = &v7[v8 + 55];
v47 = (v8 - 5) >> 2;
do
{
v30 = v40;
v31 = 4;
do
{
*v30++ = sub_402270(*v29++);
--v31;
}
while ( v31 );
v32 = v47 == 1;
v40 -= 16;
--v47;
}
while ( !v32 );
v8 = v43;
v28 = v42;
}
if ( v28 < v8 )
{
v33 = 4 * v8;
v34 = 4 * v28;
v35 = &v7[v28 + 3];
v36 = v8 - v28;
do
{
v37 = *v35;
v38 = v34 - v33;
v34 += 4;
++v35;
--v36;
*(v7 + v38 + 268) = v37;
}
while ( v36 );
}
return 1;
}