赞
踩
最近打CTF太郁闷了,总是被逆向出来自己见过但又偏偏想不起来的算法。这次总结个大个的
具体算法的原理博客上许多大佬们已经讲解的十分详细。本文不再赘述仅仅做一些加密算法特征的总结。若有错误的地方还望各位读文章的师傅们指出。
文中部分代码转载自:https://kabeor.cn
代码参考52pojie论坛的kabeo大佬,原文地址:https://www.52pojie.cn/thread-793420-1-1.html
#include <stdio.h> #include "string.h" #include "stdlib.h" const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; static char find_pos(char ch); char *base64_encode(const char* data, int data_len,int *len); /** *找到ch在base中的位置 */ static char find_pos(char ch) { //the last position (the only) in base[] char *ptr = (char*)strrchr(base, ch); return (ptr - base); } /** *BASE64编码 */ char *base64_encode(const char* data, int data_len,int *len) { //Length change to prior 3/4 int prepare = 0; int ret_len; *len=0; int temp = 0; char *ret = NULL; char *f = NULL; int tmp = 0; char changed[4]; int i = 0; ret_len = data_len / 3; //确定最后的 = temp = data_len % 3; // if (temp > 0) { ret_len += 1; } //最后一位以''结束 ret_len = ret_len*4 + 1; //加上‘\0’ ret = (char *)malloc(ret_len); //确定编码后的长度 if ( ret == NULL) { printf("No enough memory.n"); exit(0); } memset(ret, 0, ret_len); f = ret; //tmp记录data中移动位置 while (tmp < data_len) { temp = 0; prepare = 0; memset(changed, 0, 4); while (temp < 3) //每次读取三位 { if (tmp >= data_len) { break; } //将data前8*3位移入prepare的低24位 prepare = ((prepare << 8) | (data[tmp] & 0xFF));//作用是变为二进制 tmp++; temp++; //作为计数器 } //将有效数据移到以prepare的第24位起始位置 prepare = (prepare<<((3-temp)*8)); for (i = 0; i < 4 ;i++ ) { //最后一位或两位 if (temp < i) { changed[i] = 0x40; } else { //24位数据 changed[i] = (prepare>>((3-i)*6)) & 0x3F; } *f = base[ changed[ i ] ]; f++; (*len)++; } } strcpy(f,""); return ret; }
编译出文件,拖进IDA中:
void *__fastcall base64_encode(__int64 a1, int a2, _DWORD *a3) { _DWORD *v4; // [rsp+8h] [rbp-48h] char v5[4]; // [rsp+24h] [rbp-2Ch] void *s; // [rsp+28h] [rbp-28h] int i; // [rsp+30h] [rbp-20h] int v8; // [rsp+34h] [rbp-1Ch] _BYTE *v9; // [rsp+38h] [rbp-18h] int v10; // [rsp+44h] [rbp-Ch] int v11; // [rsp+48h] [rbp-8h] int v12; // [rsp+4Ch] [rbp-4h] v4 = a3; v12 = 0; *a3 = 0; s = 0LL; v9 = 0LL; v8 = 0; i = 0; v11 = a2 / 3; if ( a2 % 3 > 0 ) ++v11; v11 = 4 * v11 + 1; s = malloc(v11); if ( !s ) { printf("No enough memory.n"); exit(0); } memset(s, 0, v11); v9 = s; while ( v8 < a2 ) { v10 = 0; v12 = 0; memset(v5, 0, 4uLL); while ( v10 <= 2 && v8 < a2 ) { v12 = (v12 << 8) | *(unsigned __int8 *)(v8++ + a1); ++v10; } v12 <<= 8 * (3 - v10); for ( i = 0; i <= 3; ++i ) { if ( v10 >= i ) v5[i] = (v12 >> 6 * (3 - i)) & 0x3F; else v5[i] = 64; *v9++ = base[v5[i]]; ++*v4; } } *v9 = 0; return s; }
下面来对Base64算法的特诊进行分析:
往往数据段有编码表,如下图所示
有时候题目中会改这个表,更改里面的某些字符或者顺序。如果你碰到别(shi)出(fen)心(bian)裁(tai)的出题者把编码表给加密了,那就老老实实地去解吧…
编码过程中,读取每次读取八位,编码每次编码六位
以六位结果进行编码表的映射
Base32与Base64相似,Base32的编码表内是32个字符,每次取5位进行映射
#include <stdio.h> #include <string.h> #include <stdlib.h> //base32 表包含 0~9 以及小写字母 (去除'a','i','l','o'), //共 32 个字符 static const char base32_alphabet[32] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', }; /* * 匹配 base32_alphabet */ int find_number(char m) { int i; for(i = 0; i < 32; ++i) { if(m == base32_alphabet[i]) return i; } return -1; } /* * base32 编码 */ char* base32_encode(char *bin_source){ int i; int j = 0; static char str[10]; for(i=0;i<strlen(bin_source);++i){ if((i+1)%5==0){ j++; int num = (bin_source[i]-'0')+(bin_source[i-1]-'0')*2\ +(bin_source[i-2]-'0')*2*2+(bin_source[i-3]-'0')*2*2*2\ +(bin_source[i-4]-'0')*2*2*2*2; str[j-1] = base32_alphabet[num]; } } return str; } /* 1. base32 解码 */ int* base32_decode(char *str_source){ int i,j; static int dec[50]; int count=0; for(i=0;i<strlen(str_source);++i){ for(j=5-1;j>=0;--j){ count++; //位运算十进制转二进制 dec[count-1] = find_number(str_source[i])>>(j%5)&1; } } return dec; } int main() { char *p=base32_encode("1001101010"); printf("%s",p); return 0; }
所有的Base家族本质上是换汤不换药
Base16按照4位二进制进行编码,编码表0~9外加a ~ f,其余相似
如果说上面的算法是可以使用IDA插件FindCrypt找到的话,以下部分则需要用自己眼睛判断了
参考资料:《应用密码学》张仕斌,万武南,张金全,孙宣东著,西安电子科技大学出版社
由于网上的对于SM4算法的解释太少了,于是就参考了密码学课本…
1. 分组长度为128比特,在代码中也就是16个“字符”。密文输出同样为182比特
2. 轮密钥总共32个,每个轮密钥32比特。由初始密钥(共128bit)和系统参数(128bit)一起生成初始的key0,1,2,3。
3. 加密与解密算法完全相同,不同的是使用轮密钥的顺序不同(加密使用顺序key0,key1,key2.....key31,解密顺序key31,key30,key29....key0)
1.期初输入128bit的密钥,与系统参数128bit进行异或
2.进入循环生成器,128bit分为4组ki,ki+1,ki+2,ki+3
3.分组1,2,3进行异或,再与固定参数进行异或
4.S盒置换(书中的这部分32bit分组为32bit后又合并回去了?...本菜鸡有些小迷茫)
5.自身与自身的循环左移,循环右移进行异或
6.再与最初的ki进行异或,得到最终的结果
由轮密钥生成机制,我们至少可以总结出逆向SM4算法的如下特征:
1.在执行过程中会有一个子程序,以32为周期进行某些运算
2.会出现<<<13和<<<23,还有三到四个进行了各(jing)种(shen)移(fen)位(lie)的后的同一个数一起异或
3.数据段里起码会有个16*16的S盒,和长度为32的固定参数
下图与上面对应:
1.
2.
3.置换表
固定参数:
虽然作为一个懒人已经不想翻书了, 但到目前位置依然不足以确定算法,有S盒有异或的算法多了去了
加密过程:
其实也很简单,将送入L变换的Input进行如下操作
Output=Input^(Input<<<2)^(Input<<<10)^(Input<<<18)^(Input<<<24)
(此部分建议对照上图食用)
1.128bit的明文分为四组,每一组32bit
2.在第i轮取出Xi+1,Xi+2,Xi+3,轮密钥进行异或,并送入T变换(先S盒置换再进行L线性变换)
3.将结果与Xi置换后作为Xi+4,Xi+1至Xi+3下标不变
4.最后一轮,将X32,33,34,35顺序转为35,34,33,32
1.算法中会出现L变换
Input^(Input<<<2)^(Input<<<10)^(Input<<<18)^(Input<<<24)
通常循环左移用算法(Input<<<x)=(Input>>(32-x))|(Input<<x)代替
2.传入加密函数的其中一个值会送进某子程序进行32次的操作
参考资料:https://github.com/razvanalex/IDEA-encryption-algorithm
IDEA算法其实在算法难度上并不是很高,主要有以下几个特点
IDEA的子秘钥生成比起其他算法先得略随意一点,子秘钥函数将初始的8 * 16bits直接当做第一轮秘钥,剩余的4*16bits作为最终输出前的运算秘钥。
特征:
(x << n) | (x >> (s - n)) 等价于 x<<<n
使用IDA反汇编后的样子明文使用64bits作为输入,分为4个16bits的分组
运算过程中存在两种特殊的运算,a*b mod (216+1) 和 a+b mod(216)
a*b mod (2**16+1) 定义为:
if a == 0:
return -b + 1;
if b == 0:
return -a + 1;
if a!=0 and b!=0:
#[]为向下取整取整
if a*b mod 2**16 >= a*b/2**16 :
return a*b mod 2**16 - [a*b/2**16]
else:
return a*b mod 2**16 - a*b/2**16 +1
经过IDA反编译后:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。