rc4
rc4可执行文件下载:https://www.kanxue.com/author/attach-download-1540.htm
rc4加密算法介绍——博客传送门:
https://www.cnblogs.com/shelmean/p/14281332.html
通过静态分析很容易确定是采用rc4加密,因为rc4是流密码,且具有可逆性,我们的策略变得更加多样。如果采用的就是传统rc4加密,我们可以直接使用在线工具破解;如果程序有修改,我们除了编写变体代码(对于这种较为复杂的加密,除了一些特征明显的代换表等常量异样较容易被看出之外,有些魔改较难被发现),我们可以利用加密算法的特性,rc4是利用密钥产生加密流,然后与数据流异或,如果我们能够定位到加密流的位置,读取其值,也能够完成破解
.text:0000000000001673 push rbp
.text:0000000000001674 mov rbp, rsp
.text:0000000000001677 sub rsp, 70h
.text:000000000000167B mov rax, fs:28h
.text:0000000000001684 mov [rbp+var_8], rax
.text:0000000000001688 xor eax, eax
.text:000000000000168A mov rax, 65726F6C6C6568h
.text:0000000000001694 mov [rbp+var_58], rax
.text:0000000000001698 mov [rbp+var_60], 7
.text:00000000000016A0 mov rax, 0AF460FA5C89B9E4Ah
.text:00000000000016AA mov rdx, 815478DAE0847742h
.text:00000000000016B4 mov [rbp+s2], rax
.text:00000000000016B8 mov [rbp+var_48], rdx
.text:00000000000016BC mov rax, 6DDF471AE3E8C4D6h
.text:00000000000016C6 mov rdx, 77E3E3A5D9920A7Fh
.text:00000000000016D0 mov [rbp+var_40], rax
.text:00000000000016D4 mov [rbp+var_38], rdx
.text:00000000000016D8 mov [rbp+buf], 0
.text:00000000000016E0 mov [rbp+var_28], 0
.text:00000000000016E8 mov [rbp+var_20], 0
.text:00000000000016F0 mov [rbp+var_18], 0
通过数据的长度,我们可以大致猜测出这32字节的数字就是加密后的值,
而mov rax, 65726F6C6C6568h
大概就是密钥,但是我们也不能排除其为8字节的数据,有一个字节为0.但是查看后面的代码后,我们发现了一个常数7,打消了我们的疑虑。
但当我们直接将数据复制粘贴的时候,解密出来是乱码,原因是数据存储的大小端问题。
我们知道,小端法的格式是低位在低地址,大端法则是高位在低地址,在分析这类问题的时候其实只用抓两个点:数据是怎么存储的,计算机是怎么解释的。小端法决定了8个字节为一组的数据存储是逆向的,当我们将其视为若干个1比特的char数组时,是线性地从低地址到高地址扫描的,因此我们需要对密文数据进行调整。
那对于密钥用不用调整呢?还是要看计算机怎么解释,通过分析我们可以知道rc4在读取key的时候是将其看作字节数组,而不是一个“uint_64”之类的数字,因此我们也需要进行反转
于是我们得到正确的flag:flag{Rc4_1s_veRy_Easy_????!!!!!}
顺便一提,在使用在线解密的时候我还犯了一个很铸币的错误,没注意调整那32个字节的解释格式(应该为byte,但是起初默认是text),导致一致加密失败,后来发现它提示说加密了67个字节(应该是算上了3个换行符),我懵了,哪来的这么多字节?然后才发现......
tea
tea可执行文件下载:https://www.kanxue.com/author/attach-download-1542.htm
tea加密算法——博客传送门:
https://www.cnblogs.com/iBinary/p/13844861.html
.text:0000000000001458 mov [rbp+var_70], 2
.text:000000000000145F mov [rbp+var_6C], 2
.text:0000000000001466 mov [rbp+var_68], 3
.text:000000000000146D mov [rbp+var_64], 4
.text:0000000000001474 mov [rbp+buf], 0
.text:000000000000147C mov [rbp+var_38], 0
.text:0000000000001484 mov [rbp+var_30], 0
.text:000000000000148C mov [rbp+var_28], 0
.text:0000000000001494 mov [rbp+var_20], 0
.text:000000000000149C mov [rbp+var_18], 0
.text:00000000000014A4 mov [rbp+var_60], 0A85BBFA5h
.text:00000000000014AB mov [rbp+var_5C], 0B21C2C6Ah
.text:00000000000014B2 mov [rbp+var_58], 0E29C1A1Bh
.text:00000000000014B9 mov [rbp+var_54], 0CE7F8AAFh
.text:00000000000014C0 mov [rbp+var_50], 0AC46CCDCh
.text:00000000000014C7 mov [rbp+var_4C], 9A13C658h
.text:00000000000014CE mov [rbp+var_48], 803986D2h
.text:00000000000014D5 mov [rbp+var_44], 0AB72607Ah
分析后面的程序可知2,2,3,4是密钥,后面32字节是密文,但直接解密也是乱码,可能也进行了魔改。我们借助反编译功能来方便分析:
__int64 __fastcall encrypt(__int64 a1, _DWORD *a2)
{
int i; // [rsp+18h] [rbp-28h]
unsigned int v4; // [rsp+1Ch] [rbp-24h]
unsigned int v5; // [rsp+20h] [rbp-20h]
int v6; // [rsp+24h] [rbp-1Ch]
unsigned int j; // [rsp+28h] [rbp-18h]
for ( i = 0; i <= 3; ++i )
{
v4 = *(_DWORD *)(8LL * i + a1);
v5 = *(_DWORD *)(4 * (2 * i + 1LL) + a1);
v6 = 0;
for ( j = 0; j <= 0x1F; ++j )
{
v6 -= 1640531527;
v4 += (v5 + v6) ^ (16 * v5 + *a2) ^ ((v5 >> 5) + a2[1]);
v5 += (v4 + v6) ^ (16 * v4 + a2[2]) ^ ((v4 >> 5) + a2[3]);
}
*(_DWORD *)(a1 + 8LL * i) = v4;
*(_DWORD *)(a1 + 4 * (2 * i + 1LL)) = v5;
}
return a1;
}
(ida也可以将形式参数改名,相比a1,a2更有可读性),经过与标准代码的对比,仅有delta出现了问题,这里是sum-=1640531527 (注意这里是十进制,对应0x61C88647),我们将模板进行修改(模板来自上面的博客):
#include<cstdio>
#include<cstdlib>
void Encrypt(long* EntryData, long* Key)
{
//分别加密数组中的前四个字节与后4个字节,4个字节为一组每次加密两组
unsigned long x = EntryData[0];
unsigned long y = EntryData[1];
unsigned long sum = 0;
unsigned long delta = 0x61C88647;
//总共加密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]);
}
//最后加密的结果重新写入到数组中
EntryData[0] = x;
EntryData[1] = y;
}
void Decrypt(long* EntryData, long* Key)
{
//分别加密数组中的前四个字节与后4个字节,4个字节为一组每次加密两组
unsigned long x = EntryData[0];
unsigned long y = EntryData[1];
unsigned long sum = 0;
unsigned long delta = 0x61C88647;
sum = delta*(-32); //注意这里,sum = 32轮之后的黄金分割值. 因为我们要反序解密.
//总共加密32轮 那么反序也解密32轮
for (int i = 0; i < 32; i++)
{
// 先将y解开 然后参与运算在解x
y -= ((x << 4) + Key[2]) ^ (x + sum) ^ ((x >> 5) + Key[3]);
x -= ((y << 4) + Key[0]) ^ (y + sum) ^ ((y >> 5) + Key[1]);
sum += delta;
}
//最后加密的结果重新写入到数组中
EntryData[0] = x;
EntryData[1] = y;
}
int main()
{
unsigned long Data[8]={0xA85BBFA5,0xB21C2C6A,0xE29C1A1B,0xCE7F8AAF,0xAC46CCDC,0x9A13C658,0x803986D2,0xAB72607A};
long key[4] = { 0x2,0x2,0x3,0x4 };
Decrypt((long*)Data, key);
Decrypt((long*)(Data+2), key);
Decrypt((long*)(Data+4), key);
Decrypt((long*)(Data+6), key);
unsigned char* ptr=(unsigned char*)(Data);
for(int i=0;i<32;i++){
printf("%c",ptr[i]);
}
}
为什么这里不需要对数据进行反序呢?我们要考虑计算机对数据的解释,这里是读取的long,因此会自动“再反序一遍”,存储的反序和读取的反序相抵消,同理key也是如此
我们可以得到flag:flag{tea_is_very_easy_Rry_solve}
base64
base64.exe下载:https://www.kanxue.com/author/attach-download-1543.htm
根据函数名可知是采用base64加密
但是要注意,当ida的数据后面显示...时,说明数据仅仅截取了一部分,一定要点进去才能看到完整数据
但是直接解密后是乱码,我们猜测程序可能并没有使用传统的base64加密,我们通过检查rodata可以发现下面可疑的数据:
显然这和传统base64的代换表是不同的,我们要编写一个新的程序来解密
python可以import base64使用现成的解密方法,而mystr.translate(str.maketrans(str1,str2))可以将mystr中所有的字符按照str1->str2的顺序进行代换,我们可以借助这一点来简化代码:
import base64
import string
cipher="mTyqm7wjODkrNLcWl0eqO8K8gc1BPk1GNLgUpI=="
str1="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
str2="AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0987654321/+"
print(base64.b64decode(cipher.translate(str.maketrans(str2,str1))))
运行后得到flag:b'flag{Special_Base64_By_Lich}'
b'...'是Python自动加的,我们需要的是里面的部分
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。