RC4加密算法與逆向方法簡析


參考文章:

https://www.cnblogs.com/zibility/p/5404478.html

https://www.52pojie.cn/thread-800115-1-1.html

https://blog.csdn.net/mcmuyanga/article/details/109856288

算法分析

基本介紹

RC4 是一種流加密算法,密鑰長度可變。它加解密使用相同的密鑰,因此也屬於對稱加密算法。RC4 是有線等效加密(WEP)中采用的加密算法,也曾經是 TLS 可采用的算法之一。

加解密原理

RC4 由偽隨機數生成器和異或運算組成(由於異或運算的對合性,RC4 加密解密使用同一套算法)。RC4 的密鑰長度可變,范圍是[1,255]。RC4 一個字節一個字節地加解密。給定一個密鑰,偽隨機數生成器接受密鑰並產生一個 S 盒。S 盒用來加密數據,而且在加密過程中 S 盒會變化。

C代碼表示

幾個基本變量:

  • S-Box 也就是所謂的 S 盒,是一個 256 長度的 char 型數組,每個單元都是一個字節,算法運行的任何時候,S 都包括 0-255 的 8 比特數的排列組合,只不過值的位置發生了變換。
  • 密鑰K char key[256] 密鑰的長度 keylen 與明文長度、密鑰流的長度沒有必然關系
  • 臨時向量 k 長度也為 256,每個單元也是一個字節。如果密鑰的長度是 256 字節,就直接把密鑰的值賦給 k,否則,輪轉地將密鑰的每個字節賦給 k

初始化部分

參數 1 是一個 256 長度的 char 型數組,定義為: unsigned char sBox[256]
參數 2 是密鑰,其內容可以隨便定義:char key[256]
參數 3 是密鑰的長度,Len = strlen(key)

/*初始化函數*/
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;
    }
}

初始化長度為 256 的 S 盒。第一個 for 循環將 0 到 255 的互不重復的元素裝入 S 盒。第二個 for 循環根據密鑰打亂 S 盒,i 確保 S-box 的每個元素都得到處理,j 保證 S-box 的攪亂是隨機的。而不同的 S-box 在經過偽隨機子密碼生成算法的處理后可以得到不同的子密鑰序列,將 S-box 和明文進行 xor 運算,得到密文,解密過程也完全相同。

加密部分

參數 1 是上邊 rc4_init 函數中,被攪亂的 S-box
參數 2 是需要加密的數據 data
參數 3 是 data 的長度

/*加解密*/
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];
    }
}

每收到一個字節,就進行循環。通過一定的算法定位 S 盒中的一個元素,並與輸入字節異或,得到 k。循環中還改變了 S 盒。如果輸入的是明文,輸出的就是密文;如果輸入的是密文,輸出的就是明文。

主函數

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;
}

逆向分析

例題:BabyAlgorithm

無殼,IDA 載入,查看 main 函數

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  __int64 result; // rax
  int i; // [rsp+Ch] [rbp-E4h]
  char v5[16]; // [rsp+10h] [rbp-E0h] BYREF
  char s[64]; // [rsp+20h] [rbp-D0h] BYREF
  char v7[64]; // [rsp+60h] [rbp-90h] BYREF
  char v8[72]; // [rsp+A0h] [rbp-50h] BYREF
  unsigned __int64 v9; // [rsp+E8h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  memset(v8, 0, 0x40uLL);
  v8[0] = 0xC6;
  v8[1] = 0x21;
  v8[2] = 0xCA;
  v8[3] = 0xBF;
  v8[4] = 0x51;
  v8[5] = 0x43;
  v8[6] = 0x37;
  v8[7] = 0x31;
  v8[8] = 0x75;
  v8[9] = 0xE4;
  v8[0xA] = 0x8E;
  v8[0xB] = 0xC0;
  v8[0xC] = 0x54;
  v8[0xD] = 0x6F;
  v8[0xE] = 0x8F;
  v8[0xF] = 0xEE;
  v8[0x10] = 0xF8;
  v8[0x11] = 0x5A;
  v8[0x12] = 0xA2;
  v8[0x13] = 0xC1;
  v8[0x14] = 0xEB;
  v8[0x15] = 0xA5;
  v8[0x16] = 0x34;
  v8[0x17] = 0x6D;
  v8[0x18] = 0x71;
  v8[0x19] = 0x55;
  v8[0x1A] = 8;
  v8[0x1B] = 7;
  v8[0x1C] = 0xB2;
  v8[0x1D] = 0xA8;
  v8[0x1E] = 0x2F;
  v8[0x1F] = 0xF4;
  v8[0x20] = 0x51;
  v8[0x21] = 0x8E;
  v8[0x22] = 0xC;
  v8[0x23] = 0xCC;
  qmemcpy(&v8[0x24], "3S1", 3);
  v8[0x28] = 0x40;
  v8[0x29] = 0xD6;
  v8[0x2A] = 0xCA;
  v8[0x2B] = 0xEC;
  v8[0x2C] = 0xD4;
  puts("Input flag: ");
  __isoc99_scanf("%63s", s);
  if ( strlen(s) == 0x2D )
  {
    strcpy(v5, "Nu1Lctf233");
    sub_400874(v5, s, v7);
    for ( i = 0; i <= 0x2C; ++i )
    {
      if ( v7[i] != v8[i] )
      {
        puts("GG!");
        return 0LL;
      }
    }
    puts("Congratulations!");
    result = 0LL;
  }
  else
  {
    puts("GG!");
    result = 0LL;
  }
  return result;
}

sub_400874 如下,sub_40067A()和 sub_400753()兩個函數都是加密算法

sub_40067A()是流密鑰的生成

sub_400753()是加密

其中 sub_400646 函數的作用就相當於 swap

本題的密鑰是 Nu1Lctf233

加密后的密文是 v8,也就是

[0xc6,0x21,0xca,0xbf,0x51,0x43,0x37,0x31,0x75,0xe4,0x8e,0xc0,0x54,0x6f,0x8f,0xee,0xf8,0x5a,0xa2,0xc1,0xeb,0xa5,0x34,0x6d,0x71,0x55,0x8,0x7,0xb2,0xa8,0x2f,0xf4,0x51,0x8e,0xc,0xcc,0x33,0x53,0x31,0x0,0x40,0xd6,0xca,0xec,0xd4 ]

對它進行 utf-8 編碼得到真正的密文

import base64
a=[0xc6,0x21,0xca,0xbf,0x51,0x43,0x37,0x31,0x75,0xe4,0x8e,0xc0,0x54,0x6f,0x8f,0xee,0xf8,0x5a,0xa2,0xc1,0xeb,0xa5,0x34,0x6d,0x71,0x55,0x8,0x7,0xb2,0xa8,0x2f,0xf4,0x51,0x8e,0xc,0xcc,0x33,0x53,0x31,0x0,0x40,0xd6,0xca,0xec,0xd4]
s=""
for i in a:
    s+=chr(i)
print(s)
print(str(base64.b64encode(s.encode('utf-8')), 'utf-8'))

密文:w4Yhw4rCv1FDNzF1w6TCjsOAVG/Cj8Ouw7hawqLDgcOrwqU0bXFVCAfCssKoL8O0UcKODMOMM1MxAEDDlsOKw6zDlA==

貼上百度的腳本,拿到 flag

import base64
def rc4_main(key = "init_key", message = "init_message"):
    print("RC4解密主函數調用成功")
    print('\n')
    s_box = rc4_init_sbox(key)
    crypt = rc4_excrypt(message, s_box)
    return crypt
    
def rc4_init_sbox(key):
    s_box = list(range(256)) 
    print("原來的 s 盒:%s" % s_box)
    print('\n')
    j = 0
    for i in range(256):
        j = (j + s_box[i] + ord(key[i % len(key)])) % 256
        s_box[i], s_box[j] = s_box[j], s_box[i]
    print("混亂后的 s 盒:%s"% s_box)
    print('\n')
    return s_box
    
def rc4_excrypt(plain, box):
    print("調用解密程序成功。")
    print('\n')
    plain = base64.b64decode(plain.encode('utf-8'))
    plain = bytes.decode(plain)
    res = []
    i = j = 0
    for s in plain:
        i = (i + 1) % 256
        j = (j + box[i]) % 256
        box[i], box[j] = box[j], box[i]
        t = (box[i] + box[j]) % 256
        k = box[t]
        res.append(chr(ord(s) ^ k))
    print("res用於解密字符串,解密后是:%res" %res)
    print('\n')
    cipher = "".join(res)
    print("解密后的字符串是:%s" %cipher)
    print('\n')
    print("解密后的輸出(沒經過任何編碼):")
    print('\n')
    return  cipher

a=[0xc6,0x21,0xca,0xbf,0x51,0x43,0x37,0x31,0x75,0xe4,0x8e,0xc0,0x54,0x6f,0x8f,0xee,0xf8,0x5a,0xa2,0xc1,0xeb,0xa5,0x34,0x6d,0x71,0x55,0x8,0x7,0xb2,0xa8,0x2f,0xf4,0x51,0x8e,0xc,0xcc,0x33,0x53,0x31,0x0,0x40,0xd6,0xca,0xec,0xd4]
s=""
for i in a:
    s+=chr(i)

s=str(base64.b64encode(s.encode('utf-8')), 'utf-8')

rc4_main("Nu1Lctf233", s)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM