目录
前言
对自己逆向学习的一个小小的记录与整理,定期更新博客并回头修订
对于纯萌新来说可能会有点难度,不懂的建议私信拷打(大哭,真的不知道怎么写
第一次写这类博客,经验欠缺,请务必谅解
逆向中常见的编码和或算法
Base64,TEA,MD5,RC4(按能力学多少算多少)
AES,DES,RSA,sm4,angr,z3(受编者能力所限,暂时不讲)
参考资料:常见加密算法和编码识别 - CTF Wiki (ctf-wiki.org)
Base64
Base编码有很多:Base16,Base32,Base64,Base85,Base36,Base 58,Base91,Base 92,Base62。最重要的是Base64。
#include <iostream>
#include <cstring>
#include <stdlib.h>
using namespace std;
const char base64_table[] ="qvEJAfHmUYjBac+u8Ph5n9Od17FrICL/X0gVtM4Qk6T2z3wNSsyoebilxWKGZpRD";
void decode(string input, char* output) {
int i = 0, j = 0;
int length = input.length();
while (i < length) {
int index1 = strchr(base64_table, input[i++]) - base64_table;
int index2 = strchr(base64_table, input[i++]) - base64_table;
int index3 = strchr(base64_table, input[i++]) - base64_table;
int index4 = strchr(base64_table, input[i++]) - base64_table;
output[j++] = ((index1 << 2) | (index2 >> 4));
if (index3 != 64) {
output[j++] = (((index2 & 15) << 4) | (index3 >> 2));
if (index4 != 64) {
output[j++] = (((index3 & 3) << 6) | index4);
}
}
}
output[j] = '\0';
}
int main() {
string encoded = "a5mc58bphliax7j";
int length = (int)(encoded.length()*0.75);
// Base64 编码后的字符串长度为输入字符数的 4/3
char* decoded = (char*) malloc(length + 1); // 分配内存
decode(encoded, decoded);
cout << decoded << endl;
free(decoded);
return 0;
}
Base64是一种基于64个可打印字符来表示二进制数据的表示方法。每6个比特为一个单元,对应某个可打印字符。3个字节有24个比特,对应于4个Base64单元,即3个字节可由4个可打印字符来表示。常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据。
如果要编码的字节数不能被3整除,最后会多出1个或2个字节,那么可以使用下面的方法进行处理:1.先使用0字节值在末尾补足,使其能够被3整除,然后再进行Base64的编码。2.在编码后的Base64文本后加上一个或两个=号,代表补足的字节数。
魔改:换表+自定义结构
参考资料:
TEA
该系列共有三种算法,TEA --> XTEA -->XXTEA,从右往左依次是前面的升级版。
#include <stdio.h>
#include <stdint.h>
void encrypt (uint32_t* v, uint32_t* k) {
uint32_t sum = 0; // 注意sum也是32位无符号整型
uint32_t v0 = v[0], v1 = v[1];
uint32_t delta = 0x9e3779b9;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (int i=0; i<32; i++) {
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 decrypt (uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1];
uint32_t delta = 0x9e3779b9;
uint32_t sum = delta * 32;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (int i=0; i<32; i++) {
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;
}
// test
int main(){
// 两个32位无符号整数,即待加密的64bit明文数据
uint32_t v[2] = {0x12345678, 0x78563412};
// 四个32位无符号整数,即128bit的key
uint32_t k[4]= {0x1, 0x2, 0x3, 0x4};
printf("Data is : %x %x\n", v[0], v[1]);
encrypt(v, k);
printf("Encrypted data is : %x %x\n", v[0], v[1]);
decrypt(v, k);
printf("Decrypted data is : %x %x\n", v[0], v[1]);
return 0;
}
/*
Data is : 12345678 78563412
Encrypted data is : 9a65a69a 67ed00f6
Decrypted data is : 12345678 78563412
*/
微型加密算法(Tiny Encryption Algorithm,TEA)是一种分组加密算法,此系列算法都使用0x9E3779B9作为倍数。有时该常数会以减法的形式出现,-0x61C88647=0x9E3779B9。代码中均使用64bit(8byte)的明文作为加密数据,采用128bit(16byte)的值作为key,算法采用迭代的形式,这里采用32轮。
共同特征就是会有移位和异或的操作,逻辑、数据、个数会发生一定的变化。
魔改:自定义delta、key、数据类型和迭代轮数
参考资料:(3条消息) tea系列加密算法学习笔记_tea加密___lifanxin的博客-CSDN博客
RC4
RC4是一种流加密算法,也属于对称加密算法,密匙长度可变
//程序开始
#include<stdio.h>
#include<string.h>
typedef unsigned longULONG;
/*初始化函数*/
void rc4_init(unsigned char*s, unsigned char*key, unsigned long Len)
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i<256; i++){
s[i] = i;
k[i] = key[i%Len];
}
for (i = 0; i<256; i++){
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 Len)
{
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k<Len; k++){
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];//交换s[x]和s[y]
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] ^= s[t];
}
}
int main(){
unsigned char s[256] = { 0 }, s2[256] = { 0 };//S-box
char key[256] = { "justfortest" };
char pData[512] = "这是一个用来加密的数据Data";
unsigned long len = strlen(pData);
int i;
printf("pData=%s\n", pData);
printf("key=%s,length=%d\n\n", key, strlen(key));
rc4_init(s, (unsigned char*)key, strlen(key));//已经完成了初始化
printf("完成对S[i]的初始化,如下:\n\n");
for (i = 0; i<256; i++)
{
printf("%02X", s[i]);
if (i && (i + 1) % 16 == 0)putchar('\n');
}
printf("\n\n");
for (i = 0; i<256; i++)//用s2[i]暂时保留经过初始化的s[i]
{
s2[i] = s[i];
}
printf("已经初始化,现在加密:\n\n");
rc4_crypt(s, (unsigned char*)pData, len);//加密
printf("pData=%s\n\n", pData);
printf("已经加密,现在解密:\n\n");
//rc4_init(s,(unsignedchar*)key,strlen(key));//初始化密钥
rc4_crypt(s2, (unsigned char*)pData, len);//解密
printf("pData=%s\n\n", pData);
return 0;
}
RC4算法的原理包括初始化算法(KSA)和伪随机子密码生成算法(PRGA)两大部分。对字符数组 s 进行了初始化赋值,分别递增。之后对 s 进行了 256 次交换操作。然后通过一定的算法定位 S 盒中的一个元素,并与输入字节异或,得到 k。循环中还改变了 S 盒。
参考资料:RC4加密算法 - zbility - 博客园 (cnblogs.com)
MD5
MD5是一种哈希算法
//伪代码表示
/Note: All variables are unsigned 32 bits and wrap modulo 2^32 when calculating
var int[64] r, k
//r specifies the per-round shift amounts
r[ 0..15]:= {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22}
r[16..31]:= {5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20}
r[32..47]:= {4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23}
r[48..63]:= {6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}
//Use binary integer part of the sines of integers as constants:
for i from 0 to 63
k[i] := floor(abs(sin(i + 1)) × 2^32)
//Initialize variables:
var int h0 := 0x67452301
var int h1 := 0xEFCDAB89
var int h2 := 0x98BADCFE
var int h3 := 0x10325476
//Pre-processing:
append "1" bit to message
append "0" bits until message length in bits ≡ 448 (mod 512)
append bit length of message as 64-bit little-endian integer to message
//Process the message in successive 512-bit chunks:
for each 512-bit chunk of message
break chunk into sixteen 32-bit little-endian words w[i], 0 ≤ i ≤ 15
//Initialize hash value for this chunk:
var int a := h0
var int b := h1
var int c := h2
var int d := h3
//Main loop:
for i from 0 to 63
if 0 ≤ i ≤ 15 then
f := (b and c) or ((not b) and d)
g := i
else if 16 ≤ i ≤ 31
f := (d and b) or ((not d) and c)
g := (5×i + 1) mod 16
else if 32 ≤ i ≤ 47
f := b xor c xor d
g := (3×i + 5) mod 16
else if 48 ≤ i ≤ 63
f := c xor (b or (not d))
g := (7×i) mod 16
temp := d
d := c
c := b
b := leftrotate((a + f + k[i] + w[g]),r[i]) + b
a := temp
Next i
//Add this chunk's hash to result so far:
h0 := h0 + a
h1 := h1 + b
h2 := h2 + c
h3 := h3 + d
End ForEach
var int digest := h0 append h1 append h2 append h3 //(expressed as little-endian)
主要特征是四个magic数
0x67452301;
0xEFCDAB89;
0x98BADCFE;
0x10325476;
数据填充:1.先判断文件(消息)的大小(长度) mod 512 == 448 mod 512 ,就是大小(长度)对512求余等于448。(这里的512、448是“位”为单位,转成“字节”就是64、56,即mod 64 == 56 mod 64)2.如果大小(长度)满足 mod 512 == 448 mod 512,就在文件(消息)的末尾处添加64位(8字节)的值,值的内容是原消息的长度(以位为单位)3.如果大小(长度)不满足要求,就执行以下操作:(1)填充1个1(2)填充0,直到满足满足过程的第一步。注意:这里是以位为单位,假如是以字节为单位,第一个填充的是0x80(1000 0000),然后就填0x0
将填充完的数据以及四个magic数经过四个函数各16轮,共四个周期的加密得到结果。具体原理暂不细说,有在线解密网站