【為美好CTF獻(xiàn)上祝福】 初探密碼學(xué)
學(xué)的很淺,希望以后逆向碰上后能辨認(rèn)出是什么加密方式。
先學(xué)怎么實(shí)現(xiàn),再學(xué)原理
base64編碼
核心思想:
將 3 字節(jié) ( 24位 )的二進(jìn)制數(shù)據(jù)分割成 4 組 6 位數(shù)據(jù),然后將每個(gè) 6 位數(shù)據(jù)映射到 base64 索引表中的對(duì)應(yīng)字符。
使用 base64 需要先定義一個(gè)索引表。
一般默認(rèn)是
['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']
索引表的字符選用了 “A-Z、a-z、0-9、+、/” 64個(gè)可打印字符 (外加一個(gè)“=”作為填充符)。這是標(biāo)準(zhǔn)的 base64 協(xié)議規(guī)定。
具體轉(zhuǎn)換步驟如下:
-
把待轉(zhuǎn)換的字符串原始的二進(jìn)制數(shù)據(jù)三個(gè)字節(jié)為一組,每個(gè)字節(jié)占 8bit ,那么共有 24 個(gè)二進(jìn)制位。
-
將 24 個(gè)二進(jìn)制位 6 個(gè)為一組,分成四組。(分成四個(gè)六位的組)。
-
在每組前頭添加兩個(gè) 0 ,每組由 6 個(gè)變?yōu)?8 個(gè)二進(jìn)制位,總共 32 個(gè)二進(jìn)制位,即 4 個(gè)字節(jié)。
-
如果數(shù)據(jù)長(zhǎng)度不是 3 的倍數(shù) 用 ‘=’ 填充。
示例一: Man
舉個(gè)例子,我們現(xiàn)在把 “Man” 轉(zhuǎn)成 base64 編碼。
1.Ascll 碼值 : M(77) , a (97) , n (110) 。
2.把 Ascll碼二進(jìn)制表示。
77: 01001101
97: 01100001
110:01101110
3. 合并為 24 位 。(M、a、n 的 Ascll 碼拼接在一起)
010011010110000101101110
然后 6 位 為一組,共 4 組 (拆成四組六位)
010011/010110/000101/101110
再把二進(jìn)制轉(zhuǎn)成 10 進(jìn)制
010011 -> 19
010110 -> 22
000101 -> 5
101110 -> 46
在默認(rèn)索引表里
19 -> T
22 -> W
5 -> F
46 -> u
所以 Man 的 base64 結(jié)果為 TWFu 。
示例二: A
Ascll碼值: A (65)
轉(zhuǎn)化成二進(jìn)制: 01000001
再拆分成 4 組 6 位
010000/010
不夠 6 位 , 先補(bǔ) 0 。
010000/010000
不夠 4 組 , 缺的兩組用 = 填充 。
010000 -> 16
010000 -> 16
填充 =
填充 =
在默認(rèn)索引表中
16 -> Q
16 -> Q
=
=
因此 A 的 base64 結(jié)果為 QQ==
拿 AI 寫了一個(gè) C++ 腳本,能把字符串轉(zhuǎn)成 base64 編碼。
點(diǎn)擊查看代碼
#include <iostream>
#include <string>
static const char B64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static std::string base64_encode(const std::string& s) {
std::string out;
out.reserve(((s.size() + 2) / 3) * 4);
size_t i = 0, n = s.size();
while (i < n) {
unsigned int a = static_cast<unsigned char>(s[i++]);
unsigned int b = 0, c = 0;
bool has_b = false, has_c = false;
if (i < n) { b = static_cast<unsigned char>(s[i++]); has_b = true; }
if (i < n) { c = static_cast<unsigned char>(s[i++]); has_c = true; }
out.push_back(B64[(a >> 2) & 0x3F]);
out.push_back(B64[((a & 0x03) << 4) | (b >> 4)]);
if (has_b) {
out.push_back(B64[((b & 0x0F) << 2) | (c >> 6)]);
} else {
out.push_back('=');
}
if (has_c) {
out.push_back(B64[c & 0x3F]);
} else {
out.push_back('=');
}
}
return out;
}
int main(int argc, char** argv) {
std::string input;
if (argc > 1) { // 參數(shù)模式:把所有參數(shù)拼成一行(用空格連接)
for (int k = 1; k < argc; ++k) {
if (k > 1) input.push_back(' ');
input += argv[k];
}
} else { // 交互模式:讀取一行,回車即結(jié)束
if (!std::getline(std::cin, input)) return 0;
// 處理 Windows 的 CRLF:去掉末尾的 '\r'
if (!input.empty() && input.back() == '\r') input.pop_back();
}
std::cout << base64_encode(input) << '\n';
return 0;
}
把 base64 編碼轉(zhuǎn)換成字符串腳本如下(可支持換表):
點(diǎn)擊查看代碼
#include <iostream>
#include <string>
#include <vector>
#include <cstring>
static const char* STD_B64 =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char* URL_B64 =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
static bool build_map(const std::string& alphabet, signed char map256[256], char pad) {
if (alphabet.size() != 64) return false;
for (int i = 0; i < 256; ++i) map256[i] = -1;
// pad 不能出現(xiàn)在字母表中
for (size_t i = 0; i < alphabet.size(); ++i) {
unsigned char ch = static_cast<unsigned char>(alphabet[i]);
if (ch == static_cast<unsigned char>(pad)) return false;
if (map256[ch] != -1) return false; // 重復(fù)字符
map256[ch] = static_cast<signed char>(i);
}
return true;
}
static bool is_space(unsigned char c) {
if (c == ' ') return true;
if (c == '\t') return true;
if (c == '\n') return true;
if (c == '\r') return true;
return false;
}
static bool base64_decode_custom(const std::string& input,
const std::string& alphabet,
char pad,
std::string& out) {
signed char map256[256];
if (!build_map(alphabet, map256, pad)) {
std::cerr << "錯(cuò)誤:無(wú)效的字母表(長(zhǎng)度必須為64且不能包含填充字符)。\n";
return false;
}
// 過(guò)濾:移除空白,其余必須是字母表或 pad
std::string s;
s.reserve(input.size());
for (size_t i = 0; i < input.size(); ++i) {
unsigned char u = static_cast<unsigned char>(input[i]);
if (is_space(u)) continue;
if (u == static_cast<unsigned char>(pad)) {
s.push_back(static_cast<char>(u));
} else if (map256[u] != -1) {
s.push_back(static_cast<char>(u));
} else {
std::cerr << "錯(cuò)誤:檢測(cè)到非 Base64 字符:0x"
<< std::hex << static_cast<int>(u) << std::dec << "\n";
return false;
}
}
if (s.size() % 4 != 0) {
std::cerr << "錯(cuò)誤:去空白后的長(zhǎng)度不是4的倍數(shù)。\n";
return false;
}
out.clear();
if (!s.empty()) out.reserve((s.size() / 4) * 3);
size_t i = 0;
while (i < s.size()) {
unsigned char c0 = static_cast<unsigned char>(s[i++]);
unsigned char c1 = static_cast<unsigned char>(s[i++]);
unsigned char c2 = static_cast<unsigned char>(s[i++]);
unsigned char c3 = static_cast<unsigned char>(s[i++]);
if (c0 == static_cast<unsigned char>(pad) || c1 == static_cast<unsigned char>(pad)) {
std::cerr << "錯(cuò)誤:前兩位不允許為填充字符。\n";
return false;
}
int v0 = map256[c0];
int v1 = map256[c1];
if (v0 < 0 || v1 < 0) return false;
unsigned char b0 = static_cast<unsigned char>((v0 << 2) | (v1 >> 4));
out.push_back(static_cast<char>(b0));
if (c2 == static_cast<unsigned char>(pad)) {
// "xx==": 僅 1 字節(jié)
if (c3 != static_cast<unsigned char>(pad)) {
std::cerr << "錯(cuò)誤:'=' 使用不合法。\n";
return false;
}
break;
} else {
int v2 = map256[c2];
if (v2 < 0) return false;
unsigned char b1 = static_cast<unsigned char>(((v1 & 0x0F) << 4) | (v2 >> 2));
out.push_back(static_cast<char>(b1));
if (c3 == static_cast<unsigned char>(pad)) {
// "xxx=": 產(chǎn)生兩字節(jié)
break;
} else {
int v3 = map256[c3];
if (v3 < 0) return false;
unsigned char b2 = static_cast<unsigned char>(((v2 & 0x03) << 6) | v3);
out.push_back(static_cast<char>(b2));
}
}
}
return true;
}
int main(int argc, char** argv) {
std::string alphabet = STD_B64; // 默認(rèn)標(biāo)準(zhǔn)表
char pad = '=';
std::string b64; // 待解碼文本
// 解析參數(shù):支持 -t <表> -p <填充符> --url --std
for (int i = 1; i < argc; ++i) {
std::string a = argv[i];
if (a == "-t" && i + 1 < argc) {
alphabet = argv[++i];
} else if (a == "-p" && i + 1 < argc) {
std::string p = argv[++i];
if (p.size() != 1) {
std::cerr << "錯(cuò)誤:-p 需要單個(gè)字符作為填充符。\n";
return 2;
}
pad = p[0];
} else if (a == "--url") {
alphabet = URL_B64;
} else if (a == "--std") {
alphabet = STD_B64;
} else {
if (!b64.empty()) b64.push_back(' ');
b64 += a;
}
}
if (b64.empty()) {
if (!std::getline(std::cin, b64)) return 0;
if (!b64.empty() && b64.back() == '\r') b64.pop_back(); // 兼容 Windows CRLF
}
std::string out;
bool ok = base64_decode_custom(b64, alphabet, pad, out);
if (!ok) return 1;
std::cout.write(out.data(), static_cast<std::streamsize>(out.size()));
std::cout << '\n';
return 0;
}
base64換表
注意:base64 的索引表是可以換的,一般是對(duì)原索引表重新排序。
原表是
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
換表可能是
WHydo3sThiS7ABLElO0k5trange+CZfVIGRvup81NKQbjmPzU4MDc9Y6q2XwFxJ/
MD5加密算法
定義:
MD5(Message Digest Algorithm 5)是由Ronald Rivest于1991年設(shè)計(jì)的密碼散列函數(shù),可將任意長(zhǎng)度數(shù)據(jù)轉(zhuǎn)換為128位(16字節(jié))的固定長(zhǎng)度散列值。曾廣泛應(yīng)用于數(shù)據(jù)完整性校驗(yàn)和密碼存儲(chǔ)領(lǐng)域。
特性: 1.始終生成128位哈希值
2.本質(zhì)是哈希,具有不可逆性,無(wú)法從哈希值反推原始數(shù)據(jù)(但能爆破)。
處理流程:
- 填充數(shù)據(jù):
對(duì)需要處理的消息(字符串)進(jìn)行數(shù)據(jù)填充,使字符串的長(zhǎng)度對(duì)512取模得448,設(shè)字符串長(zhǎng)度為X,即滿足 X mod 512=448 。根據(jù)此公式得出需要填充的數(shù)據(jù)長(zhǎng)度。
填充方法:在消息后面進(jìn)行填充,填充第一位為1,其余為0。
2.添加消息長(zhǎng)度
在第一步的基礎(chǔ)上再填充上原字符串的長(zhǎng)度,可用來(lái)進(jìn)行的存儲(chǔ)長(zhǎng)度為 64 位。如果字符串長(zhǎng)度大于 \(2^{64}\) ,即使用其低于 64 位的值。 即(消息長(zhǎng)度 對(duì) \(2^{64}\) 取模)。
此步驟結(jié)束后,最終消息長(zhǎng)度就是 \(512\) 的整數(shù)倍。
3.數(shù)據(jù)處理
需要用到四個(gè)常數(shù)
A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;
以及四個(gè)函數(shù)
F(X,Y,Z)=(X & Y) | ((~X) & Z)
G(X,Y,Z)=(X & Z) | (Y & (~Z))
H(X,Y,Z)=X ^ Y ^ Z
I(X,Y,Z)=Y ^ (X | (~Z))
把消息分以512位為一分組進(jìn)行處理,每一個(gè)分組進(jìn)行4輪變換,以上面所說(shuō)4個(gè)常數(shù)為起始變量進(jìn)行計(jì)算,重新輸出4個(gè)變量,以這4個(gè)變量再進(jìn)行下一分組的運(yùn)算,如果已經(jīng)是最后一個(gè)分組,則這4個(gè)變量為最后的結(jié)果,即MD5值。
TEA加密
一種微型加密算法。
加密函數(shù)大致如下:
void Encrypt(long* EntryData, long* Key)
{
//分別加密數(shù)組中的前四個(gè)字節(jié)與后4個(gè)字節(jié),4個(gè)字節(jié)為一組每次加密兩組
unsigned long x = EntryData[0];
unsigned long y = EntryData[1];
unsigned long sum = 0;
unsigned long delta = 0x9E3779B9;
//總共加密32輪
for (int i = 0; i < 32; i++)
{
sum += delta;
x += ((y << 4) + Key[0]) ^ (y + sum) ^ ((y >> 5) + Key[1]);
y += ((x << 4) + Key[2]) ^ (x + sum) ^ ((x >> 5) + Key[3]);
}
//最后加密的結(jié)果重新寫入到數(shù)組中
EntryData[0] = x;
EntryData[1] = y;
}

浙公網(wǎng)安備 33010602011771號(hào)