小菜鸟一枚 发表于 2020-7-17 19:39

学破解第118天,《C++之RC4算法加密解密》学习

[ 本帖最后由 小菜鸟一枚 于 2020-7-17 21:07 编辑 ]\n\n## 学破解第118天,《C++之RC4算法加密解密》学习
  从小学到大专(计算机网络技术专业),玩过去的,所以学习成绩惨不忍睹,什么证书也没考,直到找不到工作才后悔,不知道怎么办才好。

  2017年12月16日,通过19元注册码注册论坛账号,开始做伸手党,潜水一年多,上来就是找软件。(拿论坛高大上的软件出去装X)

  2018年8月某一天,报名了华中科技大学网络教育本科(计算机科学与技术专业)2018级秋季。(开始提升学历)

  2019年6月17日,不愿再做小菜鸟一枚,开始零基础学习破解。(感谢小糊涂虫大哥在我刚开始学习脱壳时,录制视频解答我的问题)

  2020年7月7日,感谢H大对我的鼓励,拥有了第一篇获得优秀的文章。(接下来希望学习逆向,逆天改命)

  坛友们,年轻就是资本,和我一起逆天改命吧,我的学习过程全部记录及学习资源:(https://www.52pojie.cn/thread-1208234-1-1.html)
立帖为证!--------记录学习的点点滴滴
**本文对应视频教程地址:(https://www.52pojie.cn/thread-1221530-1-1.html)**

### 0x1RC4算法的基本介绍
  1.密钥流:RC4算法的关键是依据明文和密钥生成相应的密钥流,密钥流的长度和明文的长度是相应的。也就是说明文的长度是500字节,那么密钥流也是500字节。当然,加密生成的密文也是500字节。由密文第i字节=明文第i字节^密钥流第i字节;

  2.状态向量S:长度为256。S,S.....S。每一个单元都是一个字节。算法执行的不论什么时候。S都包含0-255的8比特数的排列组合,仅仅只是值的位置发生了变换;

  3.暂时向量T:长度也为256,每一个单元也是一个字节。

假设密钥的长度是256字节。就直接把密钥的值赋给T,否则,轮转地将密钥的每一个字节赋给T。

  4.密钥K:长度为1-256字节。注意密钥的长度keylen与明文长度、密钥流的长度没有必定关系。通常密钥的长度趣味16字节(128比特)。

### 0x2RC4算法的实现原理
  1.初始化S和T,这个时候的S显然是均匀分布的,呈线性增长,然后用密钥K对对T表进行打乱,此时T表中的数据就能用K表中的数据表示。

for i=0 to 255 do

   S=i;

   T=K[ i mod keylen ];

  2.初始排列S,用j+S+T的值对256取余,然后把值再给j,接下来用S中的值交换S中的值

j=0;

for i=0 to 255 do

   j= ( j+S+T)mod256;

   swap(S,S);

  3.产生密钥流

i,j=0;

for r=0 to len do//r为明文长度,r字节

   i=(i+1) mod 256;

   j=(j+S)mod 256;

   swap(S,S);

   t=(S+S)mod 256;

   k=S;

### 0x3RC4算法的C++实现
  1.按照上述过程,首先我要初始化S盒,作为子密钥(密钥流),然后进行加解密,解密的时候也需要它的。
```
/*
初始化函数

参数1是一个256长度的char型数组,定义为: unsigned char sBox;
参数2是密钥,其内容可以随便定义:char key;
参数3是密钥的长度,Len = strlen(key);

返回值:0表示成功
*/
int rc4_init(unsigned char*s, unsigned char*key, unsigned long Len)
{
      int i = 0, j = 0;

      //这里要定义为无符号char类型,防止出现负数,负数作为数组下标......
      unsigned char k = { 0 };
      unsigned char tmp = 0;
      for (i = 0; i < 256; i++) {
                s = i;
                k = key;
      }

      for (i = 0; i < 256; i++) {
                j = (j + s + k) % 256;
                tmp = s;//打乱S盒的值
                s = s;//交换s和s
                s = tmp;
      }
      
      //这里还应该添加代码保存子密钥,稍后会提到
      
      return 0;
}
```

  2.接下来就应该是加解密函数了,使用密钥流对明文进行异或加解密。(这里说加解密是因为两次异或后,值会还原,也就是解密了)
```
/*
加解密函数

参数1是上边rc4_init函数中,被搅乱的S盒
参数2是明文data
参数3是data的长度

返回值:0表示成功
*/
int rc4_crypt(unsigned char*s, unsigned char*data, unsigned long Len)
{
      int i = 0, j = 0, t = 0;
      unsigned char tmp;

      for (int k = 0; k < Len; k++)
      {
                i = (i + 1) % 256;
                j = (j + s) % 256;
                tmp = s;
                s = s;//交换s和s
                s = tmp;
                t = (s + s) % 256;
                data ^= s;//将明文与密钥流(打乱的s盒)进行异或加解密
      }
      return 0;
}
```

  3.现在是不是就可以进行使用RC4加密了呢?再看看,我们S和初始化后在加密函数中会再次打乱进行异或,而我们解密的时候还需要初始化时候的密钥流进行解密。所以我们应该在定义一个全局变量T盒,用来保存子密钥(密钥流),然后在初始化函数中添加代码。
```
unsigned char T = {0};

//这里还应该添加代码保存子密钥,稍后会提到
      for (int i = 0; i < 256; i++)
      {
                T = s;//字符串拷贝到全局变量T表中
      }
```

  4.现在我们就能使用RC4对字符串进行RC4加解密了。(同样,这也可以用于对文本文件加密)
```
#include "main.h"

using namespace std;

unsigned char T = { 0 };//T盒

/*
初始化函数

参数1是一个256长度的char型数组,定义为: unsigned char sBox;
参数2是密钥,其内容可以随便定义:char key;
参数3是密钥的长度,Len = strlen(key);

返回值:0表示成功
*/
int rc4_init(unsigned char*s, unsigned char*key, unsigned long Len)
{
      int i = 0, j = 0;

      //这里要定义为无符号char类型,防止出现负数
      unsigned char t = { 0 };
      unsigned char tmp = 0;
      for (i = 0; i < 256; i++) {
                s = i;//下标为几,s就对应几
                t = key;//将密钥逐个字节赋值给T盒
      }

      for (i = 0; i < 256; i++) {
                j = (j + s + t) % 256;
                tmp = s;//打乱S盒的值
                s = s;//交换s和s
                s = tmp;
      }
      //用T盒保留S盒初始化后的值
      for (int i = 0; i < 256; i++)
      {
                T = s;
                cout << "0x" << hex << (int) T << ',';
      }
      cout << endl;
      return 0;
}

/*
加解密函数

参数1是上边rc4_init函数中,被搅乱的S盒
参数2是明文data
参数3是data的长度

返回值:0表示成功
*/
int rc4_crypt(unsigned char*s, unsigned char*data, unsigned long Len)
{
      int i = 0, j = 0, t = 0;
      unsigned char tmp;

      for (int k = 0; k < Len; k++)
      {
                i = (i + 1) % 256;
                j = (j + s) % 256;
                tmp = s;
                s = s;//交换s和s
                s = tmp;
                t = (s + s) % 256;
                data ^= s;//将明文与密钥流(打乱的s盒)进行异或加解密
      }
      return 0;
}

unsigned int main()
{
      char key[] = "reverse";//密钥

      char data[] = "小菜鸟一枚";//明文

      unsigned char s;//定义S盒

      rc4_init(s, (unsigned char *)key, strlen(key));//初始化

      cout << "加密前的明文:" << data << endl;
      cout << "key:" << key << endl;
      cout << "==========RC4加解密==========" << endl;

      rc4_crypt(s, (unsigned char *)data, strlen(data));//加密
      cout << "加密后的密文:" << data << endl;

      rc4_crypt(T, (unsigned char *)data, strlen(data));//解密
      cout << "解密后的明文:" << data << endl;

      system("pause");
      return 0;
}
```

  5.最后再来看看生成的子密钥能不能配合我们的密钥将密文解密,还是声明一个长度为256的数组,然后把子密钥的十六进制形式放进去,是不是能不能解密密文。
```
unsigned char TT = {
      0x41,0x36,0x50,0x1e,0x55,0xa6,0x11,0x80,0x2c,0x63,0xe5,0x62,0xfa,0x1a,0xd3,0x6b,
      0x4,0x27,0xb0,0x42,0x12,0x3f,0xba,0x88,0xc0,0x20,0x5a,0x5f,0xed,0x6f,0x39,0x87,0x83,
      0xad,0x10,0xc9,0x19,0x1c,0x78,0x38,0xac,0x3a,0x1f,0x66,0xa5,0x9a,0x6a,0xef,
      0x9b,0xea,0x81,0xd6,0xf8,0x58,0x32,0x9d,0x8d,0x98,0x24,0xfc,0xe4,0x29,0x40,0x71,
      0x65,0x72,0x7a,0xb2,0xe6,0xb4,0x48,0xff,0xcf,0xf3,0x18,0xc1,0xc8,0xf5,0xa8,0x6c,
      0x7f,0x70,0x6e,0xe9,0x5,0x60,0xf7,0x2b,0xa2,0xf6,0xdf,0xa,0xc,0x79,0xa0,0xb5,0x91
      ,0xe8,0xd4,0x9c,0x9,0x1b,0xc6,0xb7,0x84,0x23,0xcd,0x3d,0x4b,0xa1,0x2,0x3c,0x4f,
      0xb9,0x25,0xaf,0xab,0x93,0x54,0xa4,0xca,0x9e,0x4a,0x68,0x8b,0xd5,0x21,0xa9,0xc2,
      0x4c,0x33,0x52,0x0,0x57,0x95,0xb6,0x15,0xbc,0x8c,0xd0,0x4d,0xe7,0xc4,0xe3,0x5e,
      0xae,0xfb,0xe2,0xdb,0xf0,0x51,0x94,0x3b,0x7b,0x1,0x2d,0x3,0x14,0x97,0xf9,0xbf,0xd2
      ,0x8f,0x77,0x6,0x56,0x92,0x8a,0xc3,0x74,0xd,0x47,0x31,0x73,0xec,0x2f,0x4e,0x69,
      0xc5,0xb8,0x90,0x22,0x82,0xe0,0xcc,0x28,0xdc,0xcb,0xbb,0xb,0x1d,0x7c,0xc7,0xd1,0x9f,
      0x17,0xde,0x85,0xfd,0x89,0xd8,0xbe,0x49,0xb1,0x16,0x5c,0xa7,0x99,0x3e,0x7e,0xda,
      0x45,0x76,0xce,0xf2,0x8,0xf,0x59,0x26,0xee,0xbd,0x61,0xeb,0x53,0x8e,0x64,0xdd
      ,0x7,0xd9,0xe,0xb3,0x13,0xf4,0x67,0xa3,0xf1,0x46,0x2e,0x37,0x43,0xd7,0x6d,0x44,0x96,
      0x35,0xfe,0x5b,0x5d,0x86,0x7d,0xe1,0x34,0x2a,0x30,0xaa,0x75
};

      rc4_crypt(TT, (unsigned char *)data, strlen(data));//解密
      cout << "解密后的明文:" << data << endl;
```
成功输出:小菜鸟一枚,说明我这次的学习是成功的,加解密都没问题了。

  6.补充一下,我这里为了尝试能不能直接用密钥流加解密,是使用密钥流直接进行解密的,如果有密钥,那么我们按照与加密同样的步骤,将原始密钥初始化,然后执行加解密函数进行解密,看上去很简单的算法,原理看上去还是有些迷糊,主要还是在模余和交换值的地方不知道为什么那么做。

### 0x4使用OD分析RC4加密的程序
  1.将随书文件RC4Sample.exe载入OD,程序运行起来,发现如图所示(禁用了编辑器代码,图在最后面)!


  2.当message(信息)改变后,对应的cipherTEXT(密文)也一起改变,先试试GetDlgItemTextA,不敢怎么样他也得获取文本框的内容才能运算吧,然后运行程序,对话框还没显示出来就被断下来了,不用慌,Alt+F9多来几次,返回到用户代码
```
004012BD|.C64424 1C 01mov byte ptr ss:,0x1         ; |
004012C2|.C64424 1D 23mov byte ptr ss:,0x23          ; |
004012C7|.C64424 1E 45mov byte ptr ss:,0x45          ; |
004012CC|.C64424 1F 67mov byte ptr ss:,0x67          ; |
004012D1|.C64424 20 89mov byte ptr ss:,0x89          ; |
004012D6|.C64424 21 ABmov byte ptr ss:,0xAB          ; |
004012DB|.C64424 22 CDmov byte ptr ss:,0xCD          ; |
004012E0|.C64424 23 EFmov byte ptr ss:,0xEF          ; |
004012E5|.FF15 B0604000 call dword ptr ds:[<&USER32.GetDlgItemTe>; \GetDlgItemTextA
004012EB|.8BE8          mov ebp,eax
004012ED|.85ED          test ebp,ebp
004012EF|.75 1A         jnz short RC4Sampl.0040130B
004012F1|.68 40704000   push RC4Sampl.00407040                   ; /Text = "please input name"
004012F6|.6A 6F         push 0x6F                              ; |ControlID = 6F (111.)
004012F8|.56            push esi                                 ; |hWnd = 003A0352 ('RC4 Sample',class='#32770')
004012F9|.FF15 B4604000 call dword ptr ds:[<&USER32.SetDlgItemTe>; \SetDlgItemTextA
```
不用回去跑,看寄存器窗口,因为ebp(pediy的长度)不为0,所以下面jnz会跳走
EAX:00000005
ESP:0012F4D4
而ESP+4*EAX的地址对应的字符串就是message(信息)的文本pediy

  3.继续向下走,就看到加密函数了
```
00401319|.6A 08         push 0x8
0040131B|.F3:AB         rep stos dword ptr es:
0040131D|.8D4C24 10   lea ecx,dword ptr ss:
00401321|.8D9424 180200>lea edx,dword ptr ss:
00401328|.51            push ecx                                 ;0012F4E0:pediy
00401329|.52            push edx                                 ;0012F6E8
0040132A|.E8 D1FCFFFF   call RC4Sampl.00401000                   ;初始化S盒,注意常数0x100,也就是256
0040132F|.8D4424 20   lea eax,dword ptr ss:          ;取出pediy给eax
00401333|.55            push ebp                                 ;pediy的长度入栈
00401334|.8D8C24 240200>lea ecx,dword ptr ss:         ;0012F6E8这个地址给ecx
0040133B|.50            push eax                                 ;待加密数据pediy
0040133C|.51            push ecx                                 ;S盒
0040133D|.E8 2EFDFFFF   call RC4Sampl.00401070                   ;加密函数
00401342|.83C4 18       add esp,0x18                           ;销毁局部变量
00401345|.33F6          xor esi,esi
00401347|.85ED          test ebp,ebp                           ;判断pediy长度是否为0
00401349|.7E 24         jle short RC4Sampl.0040136F            ;小于等于0跳走
0040134B|.8DBC24 140100>lea edi,dword ptr ss:
00401352|>33D2          /xor edx,edx
00401354|.8A5434 14   |mov dl,byte ptr ss:
00401358|.52            |push edx
00401359|.68 38704000   |push RC4Sampl.00407038                  ;ASCII "%02X"
0040135E|.57            |push edi
0040135F|.E8 3C000000   |call RC4Sampl.004013A0                  ;将加密后的文本变成2位16进制表示
```

  4.接下来就是把密文设置到cipherTEXT文本框中,补充一下加密函数里面几个这样的指令,要把它当成数组(或者是一连串存储空间来看)来看。
```
004010A3|.8B4CB8 08   |mov ecx,dword ptr ds:    ;0012F6E8

004010BB|.8B4C88 08   |mov ecx,dword ptr ds:    ;0012F6E8
```

### 0x5总结
  1.RC4算法采用的是异或运算,因此加密函数也就是解密函数。
  2.使用key,初始化S盒,形成密钥流,然后执行加密函数加密,每次加密都会改变S盒中的内容。
  3.破解RC4算法时,可以想办法取得加密时密钥流,初始化后的S盒,然后在执行一次加密函数,密文就自己解密了。
  4.RC4作为对称加密算法,加密前和加密后的文本长度不变,但是要想找到key还是很难的。

### 0x6参考资料:
(https://baike.baidu.com/item/RC4/3454548?fr=aladdin)
(https://www.cnblogs.com/gambler/p/9075415.html)
《加密解密第四版》P231-P233

  **PS:善于总结,善于发现,找到分析问题的思路和解决问题的办法。虽然我现在还是零基础的小菜鸟一枚,也许逆天改命我会失败,但也有着成功的可能,只要还有希望,就决不放弃!**

小菜鸟一枚 发表于 2020-7-18 10:21

aikoz88 发表于 2020-7-18 10:08
4.RC4作为对称加密算法,加密前和加密后的文本长度不变,但是要想找到key还是很难的。
对称/非对称加密算 ...

以我的学识,确实是这样理解的。
如果我的认知有误,这位朋友能否说的详细一些,谈一谈你的见解,让我学习一下!

PS:关于这个算法和破解,请留下你的高见,欢迎交流,共获提升。:handshake

aikoz88 发表于 2020-7-18 10:08

4.RC4作为对称加密算法,加密前和加密后的文本长度不变,但是要想找到key还是很难的。
对称/非对称加密算法和加解密前后的数据长度是否相同一点关系都没有!你肯定是以为对称加密的所谓对称就是加解/解密的两段数据是一样长的吧?至于破解,呵呵!你懂的!

细水流长 发表于 2020-7-17 19:44

小菜鸟一枚 发表于 2020-7-17 19:47

细水流长 发表于 2020-7-17 19:44
同是18级学生,为何你如此优秀

因为你读的是一流大学,我读的是个大专,毕业后你赚大钱,我这大专毕业好多年,还在下面打杂,累死累活挣点小钱,这就是区别,你是科班,我是野鸡大学:lol,所以,只要你想学,很定很快就能超过我了。:handshake

零下灼冰 发表于 2020-7-17 20:34

加油鸭,(ง •̀_•́)ง

乐灿峰 发表于 2020-7-17 21:04

................   118天还在玩这个?

小菜鸟一枚 发表于 2020-7-17 21:11

乐灿峰 发表于 2020-7-17 21:04
................   118天还在玩这个?
所以没看到我最后一句话说了,我还是小菜鸟一枚呀:lol

835228 发表于 2020-7-17 23:14

厉害了看不懂

icbcxcxxcx 发表于 2020-7-18 08:21

努力总会有回报!楼主,加油{:301_993:}
页: [1] 2
查看完整版本: 学破解第118天,《C++之RC4算法加密解密》学习