安全矩阵

 找回密码
 立即注册
搜索
查看: 6756|回复: 0

RC4、Base64魔改看雪CTF-变形金刚学习笔记

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2020-11-16 10:03:30 | 显示全部楼层 |阅读模式
原文链接:RC4、Base64魔改看雪CTF-变形金刚学习笔记

前言
最近在研究某某app的数据库,发现自己在so层的调试比较薄弱,专门找了看雪的CTF-变形金刚来学习,也拜读了几位大佬的文章。希望在用ida调试so方面有所突破。


工具准备

ida7.0
Transformers.apk
idea
jadx
frida
小米4root手机(android6.0)

app分析目标



为了熟悉该app,先介绍一下大神分析后的结果。


输入错误密码截图

比如输入密码是:12345678,会提示错误信息:Transformers:error

输入正确密码截图

输入密码(长度16个字符)是:fu0kzHp2aqtZAuY6,会提示错误信息:Transformers:flag{android4-9}。

静态分析-java


把Transformers.apk拖入jadx

障眼法1:该app设计作者采用了障眼法,很容易欺骗分析人员,让我们认为OnClick的回调处理逻辑在MainActivity中。

障眼法2:在MainActivity的OnCreate中欺骗代码注册OnClick回调。主要原理是利用Activity的onStart事件会晚与OnCreate事件,从而实现在OnStart中注册的OnClick回调覆盖了OnCreate中注册的回调。



Activity基类的onStart才是真正注册OnClick回调的地方:



Base64解码图

为了方便理解eq中涉及的魔改的Base64编码,这里先贴上一直Base64解码的草稿图。


下图是Base64编码4个字符'{6*的解码草稿图,画的不是很好,主要是方便我自己理解写解码Base64的逻辑代码。

它是用64个可打印字符表示二进制所有数据方法。由于2的6次方等于64,所以可以用每6个位元为一个单元,对应某个可打印字符。我们知道三个字节有24个位元,就可以刚好对应于4个Base64单元,即3个字节需要用4个Base64的可打印字符来表示。



静态分析与动态调试 跟踪liboo000oo.so
这里将分3个关键步骤:
1. JNI_OnLoad分析动态注册eq对应的函数sub_784,稍后再详细介绍。2. fini_array部分跟踪,主要是eq函数用到的一下基础数据,比如Base64编码表、RC4加密算法用到的key初始值,之后它会通过一系列异或处理算出真正的密钥key。也稍后再详细介绍。3. eq函数的算法跟踪分析。
这里先列一下分析结果:

  1. JNI_onLoad部分==========
  2.     eq是由sub_784动态注册
  3.      
  4. ini_array部分==========

  5.     #解码36长度字符串byte_4020:
  6.     byte_4020 =650f909c-7217-3647-9331-c82df8b98e98+0x00(结束符)


  7.     #解码Base64为的64+1个编码字符byte_4050=
  8.     base64Chars = byte_4050 =!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';+0x00(结束符)

  9.     #app伪装java类的名称byte_40A0:
  10.     className_Sign= android/support/v7/app/AppCompiatActivity +0x00(结束符)


  11. sub_784部分============
  12.      
  13.     获取RC4的密钥算法============:
  14.         v5=算法01 = 删除(byte_4040中的"-") =650f909c721736479331c82df8b98e98
  15.         v4=算法02 =删除byte_4020中的”-“后,特殊运算又加上”-“符号=89e89b8f-d28c-1339-7463-7127c909f056
  16.      
  17.     自定义算法03==========
  18.     v6=v17=自定义算法03 = 特殊算法处理后 =36f36b3c-a03e-4996-8759-8408e626c215  
  19.         算法参数01:v4
  20.         算法参数02:unk_23DE(10位长度) =2409715836
  21.         算法参数03:unk_23D8(16位长度)=dbeafc2409715836
  22.      
  23.     RC4魔改部分======   (已经逆向算法)
  24.         RC4第1步:v48:拷贝&unk_B4D863E8中的256个字符到v48(应视是RC4算法的魔改数据)
  25.         RC4第2步:v49:计算RC4 的临时256自己T向量,公式:iK[i]=(byte)aKey.charAt((i % aKey.length()));
  26.         RC4第3步:V48状态向量S进行置换操作V48
  27.         RC4第4步:RC4产生密钥流iOutputChar,然后return new String(iOutputChar)
  28.             v27 =33 ,即v6[3];
  29.             
  30.         RC4第5步:产生密钥流--其他还结合魔改Base64运算
  31.             分配输入密码对应的base64字节的存储空间
  32.             Base64算法的字典
  33.             
  34.     Base64魔改部分======(已经逆向算法)
  35.         Base64的字典部分-64个字符+1个结束符
  36.         每4个字符中第0个与0x6异或,第0个与0xF异或
  37.              switch (i%4){
  38.                 case 0:
  39.                     base64char = (char) (iAscii ^0x07);
  40.                     break;

  41.                 case 2:
  42.                     base64char = (char) (iAscii ^0xF);
  43.                     break;
  44.                 default:
  45.                     base64char =encodeChars[i];

  46.             }      
复制代码


NI_OnLoad 代码逻辑分析RegisterNatives


1. 搜索JNI_OnLoad,双击进入函数体

2. 导入Jni.h文件。菜单路径:ida/File/Load file/Parse C header file
3. 修改参数类型为_JavaVM
4. 按g快捷键直接跳转到0x4014


off_4010即可看到注册的函数。

找到eq注册对应的sub_784函数:

当然frida的hook_art_so_register.js脚本快速定位eq对应sub_784。

该脚本通过hook art.so 的register函数,打印动态注册的地址:
  1. //调用方式==测试ok
  2. frida -U --no-pause -f package_name -l hook_art_so_register.js
复制代码
或直接在fini_array初始化数据中可以看到。


跟踪.init_array

另外根据so文件的加载流程应该是先加载init_array,然后是JNI_OnLoad

ida 中ctrl+s 进入init_array,会先执行.datadiv_decode5009363700628197108,其实就是通过一些运算得到一些初始化数据。



init_array

按f5 采用伪代码方式查看datadiv_decode5009363700628197108,根据得到一些数据。

1. 解码36长度字符串byte_4020:650f909c-7217-3647-9331-c82df8b98e98+0x00(结束符)

2. 魔改Base64编码的table表,为的64+1个编码字符byte_4050= !:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';+0x00(结束符)

eq函数的分析 (即sub_784函数)
ida定位到sub_784,修改参数类型,并做了动态调试后,梳理sub_784的逻辑。




unk_23E8字符串(RC4魔改算法的初始化256个字符S状态向量)

unk_23E8字符串Base64的table 拷贝:




  1. unk_23E8=Sbox = [0xD7,0xDF,0x02,0xD4,0xFE,0x6F,0x53,0x3C,0x25,0x6C,0x99,0x97,0x06,0x56,0x8F,0xDE,0x40,0x11,0x64,0x07,0x36,0x15,0x70,0xCA,0x18,0x17,0x7D,0x6A,0xDB,0x13,0x30,0x37,0x29,0x60,0xE1,0x23,0x28,0x8A,0x50,0x8C,0xAC,0x2F,0x88,0x20,0x27,0x0F,0x7C,0x52,0xA2,0xAB,0xFC,0xA1,0xCC,0x21,0x14,0x1F,0xC2,0xB2,0x8B,0x2C,0xB0,0x3A,0x66,0x46,0x3D,0xBB,0x42,0xA5,0x0C,0x75,0x22,0xD8,0xC3,0x76,0x1E,0x83,0x74,0xF0,0xF6,0x1C,0x26,0xD1,0x4F,0x0B,0xFF,0x4C,0x4D,0xC1,0x87,0x03,0x5A,0xEE,0xA4,0x5D,0x9E,0xF4,0xC8,0x0D,0x62,0x63,0x3E,0x44,0x7B,0xA3,0x68,0x32,0x1B,0xAA,0x2D,0x05,0xF3,0xF7,0x16,0x61,0x94,0xE0,0xD0,0xD3,0x98,0x69,0x78,0xE9,0x0A,0x65,0x91,0x8E,0x35,0x85,0x7A,0x51,0x86,0x10,0x3F,0x7F,0x82,0xDD,0xB5,0x1A,0x95,0xE7,0x43,0xFD,0x9B,0x24,0x45,0xEF,0x92,0x5C,0xE4,0x96,0xA9,0x9C,0x55,0x89,0x9A,0xEA,0xF9,0x90,0x5F,0xB8,0x04,0x84,0xCF,0x67,0x93,0x00,0xA6,0x39,0xA8,0x4E,0x59,0x31,0x6B,0xAD,0x5E,0x5B,0x77,0xB1,0x54,0xDC,0x38,0x41,0xB6,0x47,0x9F,0x73,0xBA,0xF8,0xAE,0xC4,0xBE,0x34,0x01,0x4B,0x2A,0x8D,0xBD,0xC5,0xC6,0xE8,0xAF,0xC9,0xF5,0xCB,0xFB,0xCD,0x79,0xCE,0x12,0x71,0xD2,0xFA,0x09,0xD5,0xBC,0x58,0x19,0x80,0xDA,0x49,0x1D,0xE6,0x2E,0xE3,0x7E,0xB7,0x3B,0xB3,0xA0,0xB9,0xE5,0x57,0x6E,0xD9,0x08,0xEB,0xC7,0xED,0x81,0xF1,0xF2,0xBF,0xC0,0xA7,0x4A,0xD6,0x2B,0xB4,0x72,0x9D,0x0E,0x6D,0xEC,0x48,0xE2,0x33]
复制代码

byte_24E8指向的内容:



函数return前的逻辑:


v49字符串-RC4魔改的临时向量T:

最终是算出来Base64编码的v28_new_base64与v41的字符比较({9*8ga*l!Tn?@#fj'j$\g;;)

把sub_784业务逻辑直接采用在ida中注解的方式修订如下:

  1. int __fastcall sub_784(_JNIEnv *a1_env, int a2, void *a3_str_app_password)
  2. {
  3.   size_t v3_4020_RC4_key_length; // r10
  4.   unsigned __int8 *v4_4020_del_handle02; // r6
  5.   _BYTE *v5_4020_del_hyphen_handle01; // r8
  6.   _BYTE *v6_byte_4020_handle3; // r11
  7.   int v7; // r0
  8.   size_t v8; // r2
  9.   char *v9; // r1
  10.   int v10; // r3
  11.   int v11_len; // r1
  12.   unsigned int v12; // r2
  13.   int v13; // r3
  14.   int v14; // r0
  15.   int v15_forJump_; // r4
  16.   unsigned __int8 v16; // r0
  17.   _BYTE *v17_4020_handle3_ptr; // r3
  18.   _BYTE *v18_tmp_byte_move; // r5
  19.   char *v19_v49_256_Rc4_T; // r4
  20.   int v20; // r5
  21.   int v21; // r1
  22.   int v22; // r0
  23.   signed int v23; // r1
  24.   int v24_S; // r2
  25.   size_t v25_app_password_length; // r0
  26.   unsigned int v26_app_password_length; // r8
  27.   unsigned int v27; // r5
  28.   _BYTE *v28_new_base64; // r0
  29.   int v29; // r3
  30.   int v30; // r10
  31.   unsigned int v31_pw_index; // r2
  32.   int v32; // r12
  33.   bool v33_isAppPW_index01; // zf
  34.   _BYTE *v34_ret; // r1
  35.   bool v35; // zf
  36.   int v36; // r3
  37.   int v37_tmp; // r1
  38.   unsigned __int8 v38_char_needTodoBase64; // r11
  39.   unsigned int v39; // lr
  40.   char v40; // r1
  41.   char *v41; // r2
  42.   int v42; // t1
  43.   unsigned int v44_new_base64_length; // [sp+4h] [bp-234h]
  44.   unsigned int v45_by_app_password_length; // [sp+8h] [bp-230h]
  45.   unsigned int v46; // [sp+10h] [bp-228h]
  46.   char *s_char_app_password; // [sp+14h] [bp-224h]
  47.   char v48_256_Rc4_S[256]; // [sp+18h] [bp-220h] RC4的S向量
  48.   char v49_256_Rc4_T[256]; // [sp+118h] [bp-120h] RC4的T向量
  49.   int v50; // [sp+218h] [bp-20h]

  50.   s_char_app_password = (char *)a1_env->functions->GetStringUTFChars(&a1_env->functions, a3_str_app_password, 0);
  51.   v3_4020_RC4_key_length = strlen(byte_4020);   // 长度0x24,即36长度
  52.   v4_4020_del_handle02 = (unsigned __int8 *)malloc(v3_4020_RC4_key_length);// byte_4020被删除"-"符号,再经过特殊处理,同时有添加回新的“-”符号
  53.                                                 // 新的结果:89e89b8f-d28c-1339-7463-7127c909f056
  54.   v5_4020_del_hyphen_handle01 = malloc(v3_4020_RC4_key_length);// byte_4020中的内容删除“-” :650f909c721736479331c82df8b98e98
  55.   v6_byte_4020_handle3 = malloc(v3_4020_RC4_key_length);
  56.   _aeabi_memclr(v4_4020_del_handle02, v3_4020_RC4_key_length);
  57.   _aeabi_memclr(v5_4020_del_hyphen_handle01, v3_4020_RC4_key_length);
  58.   _aeabi_memclr(v6_byte_4020_handle3, v3_4020_RC4_key_length);
  59.   if ( v3_4020_RC4_key_length )
  60.   {
  61.     v7 = 0;
  62.     v8 = v3_4020_RC4_key_length;
  63.     v9 = byte_4020;                             // 指向36个字符串长度,在调试.fini_array已经得到:650f909c-7217-3647-9331-c82df8b98e98
  64.     do                                          // 其实就是把数据byte_4020的asci码copy到v5中byte数组
  65.     {
  66.       v10 = (unsigned __int8)*v9++;             // 取一个字符转为asci码
  67.       if ( v10 != 45 )                          // "-"的asci=45,其实就是删除“-”符号,复制byte_4020(包括“-”符号)
  68.         v5_4020_del_hyphen_handle01[v7++] = v10;
  69.       --v8;                                     // 及长度减一
  70.     }
  71.     while ( v8 );                               // 循环体结束后v5=删除(byte_4040中的"-") =650f909c721736479331c82df8b98e98
  72.                                                 // ===========================算法处理01


  73.     if ( v7 >= 1 )                              // v7 =0x20;即字符串32为长度,其实就是36位byte_4020删除“-”后的长度
  74.     {
  75.       v11_len = v7 - 1;
  76.       v12 = -8;                                 // 0xFFFFFFF8,调试>>结果发现这里应该不是-8,而是一个很大的数


  77.       v13 = 0;
  78.       v14 = 0;

  79.       do
  80.       {
  81.         if ( (v13 | (v12 >> 2)) > 3 )           // v12>>2 = -2?后第一次=0x3FFFFFFE
  82.         {
  83.           v15_forJump_ = v14;
  84.         }
  85.         else
  86.         {
  87.           v15_forJump_ = v14 + 1;
  88.           v4_4020_del_handle02[v14] = 45;       // 其实就是“-”字符的asc码45
  89.         }
  90.         v16 = v5_4020_del_hyphen_handle01[v11_len--];// v5其实就是byte_4020删除了“-”符号后的:650f909c721736479331c82df8b98e98
  91.         v13 += 0x40000000;
  92.         v4_4020_del_handle02[v15_forJump_] = v16;
  93.         ++v12;
  94.         v14 = v15_forJump_ + 1;                 // 其实也就是跳过"-"
  95.       }
  96.       while ( v11_len != -1 );                  // 该循环体计算v4的结果是:89e89b8f-d28c-1339-7463-7127c909f056
  97.                                                 // 处理逻辑是:byte_4020被删除"-"符号,再经过特殊处理,同时有添加回新的“-”符号
  98.                                                 // ===========================算法处理02



  99.       if ( v15_forJump_ >= 0 )                  // 第一次变量是0x23,即36-1
  100.       {
  101.         v17_4020_handle3_ptr = v6_byte_4020_handle3;// 初始是0x00,v17计算后指向RC4的key:36f36b3c-a03e-4996-8759-8408e626c215
  102.         while ( 1 )
  103.         {
  104.           v18_tmp_byte_move = (_BYTE *)*v4_4020_del_handle02;// ascii的hex:第一次 =0x38(对应字符"8");第2次 =0x39(对应字符"9");就是v4逐个字节取出
  105.           if ( (unsigned __int8)((_BYTE)v18_tmp_byte_move - 97) <= 5u )// ascii(97)='a';ascii(48)='0'
  106.             break;
  107.           if ( (unsigned __int8)((_BYTE)v18_tmp_byte_move - 48) <= 9u )
  108.           {
  109.             v18_tmp_byte_move = (char *)&unk_23DE + (_DWORD)v18_tmp_byte_move - 48;// unk_23DE(10位长度)指向:2409715836
  110.             goto LABEL_18;
  111.           }
  112. LABEL_19:
  113.           *v17_4020_handle3_ptr++ = (_BYTE)v18_tmp_byte_move;// 存放到v17指向的地址
  114.           --v14;                                // 本if语句中初始是0x24,即36
  115.           ++v4_4020_del_handle02;               // 移动v4指向的指针,逐个取出
  116.           if ( !v14 )                           // 如果为0退出循环体???
  117.                                                 // if语句内部的循环体;但还在if语句内
  118.             goto LABEL_20;
  119.         }                                       // end for while ( 1 )
  120.                                                 //

  121.         v18_tmp_byte_move = (char *)&unk_23D8 + (_DWORD)v18_tmp_byte_move - 97;// unk_23D8(16位长度)好像是什么密码指向:dbeafc2409715836
  122. LABEL_18:
  123.         LOBYTE(v18_tmp_byte_move) = *v18_tmp_byte_move;// 这个是IDA的常用宏,相当于取变量的最低byte位来赋值
  124.         goto LABEL_19;
  125.       }                                         // end if ( v15_forJump_ >= 0 )
  126.                                                 // ??????这个if语句里到底处理了哪些逻辑
  127.                                                 // 本if语句结束用于计算v6
  128.                                                 // 计算结束后
  129.                                                 //    v6  = 36f36b3c-a03e-4996-8759-8408e626c215
  130.                                                 //    v17 = v6的指针进行移动
  131.                                                 //    v18 = v4的字节逐个取出。(会与unk_23D8的内容进行运算)
  132.                                                 // 涉及参数:
  133.                                                 //    1:v4,即算法1-2步骤后(byte_4020):去”-“后计算又重新加上”-“符号=89e89b8f-d28c-1339-7463-7127c909f056
  134.                                                 //    2:unk_23DE(10位长度)指向:2409715836
  135.                                                 //    3:unk_23D8(16位长度)指向:dbeafc2409715836
  136.                                                 // ===========================算法处理03--获取RC4的密钥



  137.     }                                           // end if ( v7 >= 1 )
  138.   }                                             // end for if ( v3_RC4_key_length ) 处理后得到key*v17(即v6) 是:36f36b3c-a03e-4996-8759-8408e626c215
  139. LABEL_20:
  140.   _aeabi_memcpy8(v48_256_Rc4_S, &unk_23E8, 256);// 用于RC4魔改算法(魔改)-256字节初始化:
  141.                                                 // unk_23E8指向的是256个字符:-拷贝到v48
  142.                                                 // 256字符不会重复
  143.                                                 // bytes:0xD7 0xDF 0x02 0xD4 0xFE 0x6F 0x53 0x3C ....
  144.                                                 // chars:.....oS<%l...V....d.6.p.
  145.                                                 // R0 =v48地址=0xBEE74D38
  146.                                                 // =================算法处理04===RC4-01初始化256个字节的状态向量
  147.   v19_v49_256_Rc4_T = v49_256_Rc4_T;
  148.   v20 = 0;
  149.   do
  150.   {
  151.     sub_D20(v20, v3_4020_RC4_key_length);       // ???具体做了哪些工作需要跟进去看看
  152.     v49_256_Rc4_T[v20++] = v6_byte_4020_handle3[v21];// 根据算出的key再转换临时向量T
  153.   }
  154.   while ( v20 != 256 );                         // 运算v49的值--RC4魔改的另外一个256字节向量-临时向量T
  155.                                                 // 循环体算法是:
  156.                                                 //         for (short i= 0;i<256;i++)//初始化S和T(根据密钥mkey)
  157.                                                 //         {
  158.                                                 //             iK[i]=(byte)aKey.charAt((i % aKey.length()));//i%密钥长度(取值范围为1-256);
  159.                                                 //         }
  160.                                                 //
  161.                                                 // =================参数:RC4密钥长度,即base_4020长度
  162.                                                 // 循环体结束后===
  163.                                                 //   v48:内容没有被修改
  164.                                                 //   v49(只复制256字符) =36f36b3c-a03e-4996-8759-8408e626c21536f36b3c-a03e-4996-8759-8408e626c21536f36b3c-a03e-4996-8759-8408e626c21536f36b3c-a03e-4996-8759-8408e626c21536f36b3c-a03e-4996-8759-8408e626c21536f36b3c-a03e-4996-8759-8408e626c21536f36b3c-a03e-4996-8759-8408e626c21536f3
  165.                                                 // =================算法处理04-===RC4-02初始化256个字节的临时向量
  166.                                                 //
  167.                                                 //
  168.   v22 = (unsigned __int8)(v49_256_Rc4_T[0] - 41);// #0x33(即51)-41=0xA(即10)
  169.                                                 // 正常的RC4这里是0;也就是说这里也被魔改了****
  170.   v48_256_Rc4_S[0] = v48_256_Rc4_S[v22];
  171.   v48_256_Rc4_S[v22] = -41;
  172.   v23 = 1;                                      // 正常的RC4这里是0;也就是说这里也被魔改了1;也就是第一个byte不处理***
  173.   do                                            // RC4向量--初始排列S
  174.   {
  175.     v24_S = (unsigned __int8)v48_256_Rc4_S[v23];
  176.     v22 = (v22 + (unsigned __int8)v49_256_Rc4_T[v23] + v24_S) % 256;
  177.     v48_256_Rc4_S[v23++] = v48_256_Rc4_S[v22];
  178.     v48_256_Rc4_S[v22] = v24_S;
  179.   }
  180.   while ( v23 != 256 );                         // =================算法处理04===RC-03===开始对状态向量S进行置换操作(用来打乱初始种子1)
  181.                                                 //    其实就是打乱v48
  182.                                                 // 魔改代码:
  183.                                                 //
  184.                                                 //         j=10; //======注意=====正常这里是0,不过魔改的2019的CTF=10?????????
  185.                                                 //         iS_48[0] =iS_48[j];//======注意=====这段是魔改零添加的
  186.                                                 //         iS_48[j] = -41;  //======注意=====这段是魔改零添加的
  187.                                                 //
  188.                                                 //
  189.                                                 //         for (int i=1;i<255;i++)//初始排列for (int i=0;i<255;i++) //2019的CTF魔改for (int i=1;i<256;i++)
  190.                                                 //         {
  191.                                                 //             j=(j+iS_48[i]+iK_49[i]) % 256;
  192.                                                 //             int temp = iS_48[i];
  193.                                                 //             iS_48[i]=iS_48[j];
  194.                                                 //             iS_48[j]=temp;
  195.                                                 //         }



  196.   v25_app_password_length = strlen(s_char_app_password);// 就是解密数组密码的字符串:"12345678".lenght =8
  197.   v26_app_password_length = v25_app_password_length;
  198.   v27 = (unsigned __int8)v6_byte_4020_handle3[3];// v27=0x33 =51
  199.   v45_by_app_password_length = 8 * (3 - -3 * (v25_app_password_length / 3));
  200.   v44_new_base64_length = v27 + v45_by_app_password_length / 6;
  201.   v28_new_base64 = malloc(v44_new_base64_length + 1);// 分配新的base64需要使用的字节。
  202.                                                 // //==========Base64分配需要新的字节大小



  203.   if ( v26_app_password_length )                // app输入的密码长度
  204.   {
  205.     v30 = 0;
  206.     v31_pw_index = 0;                           // 循环体的index
  207.     v32 = 0;
  208.     v46 = v27;
  209.     do
  210.     {
  211.       v30 = (v30 + 1) % 256;                    // // RC4 算法的产生密钥流循环体开始
  212.       v37_tmp = (unsigned __int8)v48_256_Rc4_S[v30];
  213.       v32 = (v32 + v37_tmp) % 256;
  214.       v48_256_Rc4_S[v30] = v48_256_Rc4_S[v32];
  215.       v48_256_Rc4_S[v32] = v37_tmp;             // ==========刚刚重新分析到这里,苦逼呀

  216.       v19_v49_256_Rc4_T = (char *)(unsigned __int8)v48_256_Rc4_S[v30];
  217.       v38_char_needTodoBase64 = v48_256_Rc4_S[(unsigned __int8)(v37_tmp + (_BYTE)v19_v49_256_Rc4_T)] ^ s_char_app_password[v31_pw_index];//
  218.                                                 // ===========Base64内嵌在RC4的内部
  219.                                                 // 先线程一个RC4加密后的字符
  220.                                                 // 后面用这个字符进行Base64的解码操作
  221.                                                 // Base64魔改的算法:
  222.                                                 //   1:Base64 字典被替换
  223.                                                 //   2:Base最后以为“=”会被替换成“;”
  224.                                                 //   3: 对特定字符进行异或操作.每4个字符中第0个与0x6异或,第0个与0xF异或
  225.                                                 //     switch (i%4){
  226.                                                 //                 case 0:
  227.                                                 //                     base64char = (char) (iAscii ^0x07);
  228.                                                 //                     break;
  229.                                                 //
  230.                                                 //                 case 2:
  231.                                                 //                     base64char = (char) (iAscii ^0xF);
  232.                                                 //                     break;
  233.                                                 //                 default:
  234.                                                 //                     base64char =encodeChars[i];
  235.                                                 //
  236.                                                 //             }


  237.       if ( v31_pw_index
  238.         && (v29 = 2863311531u * (unsigned __int64)v31_pw_index >> 32, v39 = 3 * (v31_pw_index / 3), v39 != v31_pw_index) )//
  239.                                                 // 逗号运算符是指在C语言中,多个表达式可以用逗号分开,其中用逗号分开的表达式的值分别结算,但整个表达式的值是最后一个表达式的值。
  240.                                                 // 影响结果:
  241.                                                 //   false:其实就是index/3(取摸)!=0
  242.                                                 //   true: index/3 =0
  243.                                                 //   v39:index可以是3的最大倍数,0,3,6,9,12...
  244.                                                 //      index=3,则v39=3;index =6,则v39=6;index=7,则v39=6
  245.       {
  246.         v33_isAppPW_index01 = v31_pw_index == 1;// 是否是appPassword[1]字符
  247.         if ( v31_pw_index != 1 )
  248.           v33_isAppPW_index01 = v39 + 1 == v31_pw_index;
  249.         if ( v33_isAppPW_index01 )              // 判断index%3=1的情况 ,即1,4,7,10
  250.         {
  251.           v34_ret = byte_4050;                  // v34第一次=0x33(51);
  252.                                                 // byte_4050:存放的是base64的table字典字符64+1个
  253.                                                 // byte_4050(字符长度是64+1):!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';
  254.           v28_new_base64[v46 + v31_pw_index] = byte_4050[(unsigned __int8)v28_new_base64[v46 + v31_pw_index] | ((unsigned int)v38_char_needTodoBase64 >> 4)];
  255.           v19_v49_256_Rc4_T = &v28_new_base64[v46 + v31_pw_index];
  256.           v29 = 4 * v38_char_needTodoBase64 & 0x3C;// 0x3C =60 ,即111100二进制
  257.           v19_v49_256_Rc4_T[1] = v29;
  258.           if ( v31_pw_index + 1 >= v26_app_password_length )
  259.             goto LABEL_53;                      // 跳转到程序的倒数第2个Lable
  260.         }
  261.         else                                    // index%3=2的情况 2,5,8,11....
  262.         {
  263.           v35 = v31_pw_index == 2;
  264.           if ( v31_pw_index != 2 )
  265.             v35 = v39 + 2 == v31_pw_index;
  266.           if ( v35 )
  267.           {
  268.             v19_v49_256_Rc4_T = (char *)(v38_char_needTodoBase64 & 0xC0);// 0xC0 =192即11000000二进制,保留最高2bit
  269.             v36 = v46++ + v31_pw_index;
  270.             v28_new_base64[v36] = byte_4050[(unsigned __int8)v28_new_base64[v36] | ((unsigned int)v19_v49_256_Rc4_T >> 6)] ^ 0xF;// 0xf=15 ,即二进制1111。
  271.                                                 // 这里异或好像是对Base64模拟的多余操作
  272.             v29 = (int)&v28_new_base64[v36];
  273.             *(_BYTE *)(v29 + 1) = byte_4050[v38_char_needTodoBase64 & 0x3F];// 0x3f=63 即111111二进制
  274.           }
  275.         }
  276.       }
  277.       else                                      // index/%3 =0 即0,3,6,9...
  278.       {
  279.         v28_new_base64[v46 + v31_pw_index] = byte_4050[(unsigned int)v38_char_needTodoBase64 >> 2] ^ 7;// 7的二进制四111
  280.                                                 // 这里对base64进行了魔改操作
  281.         v19_v49_256_Rc4_T = &v28_new_base64[v46 + v31_pw_index];
  282.         v29 = 16 * v38_char_needTodoBase64 & 0x30;// 0x30=48,即二进制110000bit
  283.         v19_v49_256_Rc4_T[1] = v29;
  284.         if ( v31_pw_index + 1 >= v26_app_password_length )// 最后一位
  285.         {
  286.           v40 = byte_4050[v29];
  287.           *((_WORD *)v19_v49_256_Rc4_T + 1) = 15163;
  288.           goto LABEL_43;
  289.         }
  290.       }
  291.       ++v31_pw_index;
  292.     }                                           // end for if ( v26_app_password_length ) 内部的do
  293.     while ( v31_pw_index < v26_app_password_length );
  294.   }                                             // end if ( v26_strText_length ) 输入密码lenght>0
  295.                                                 // ========这是时RC4-04部分的算法“产生密钥流”
  296.                                                 //         v28_new_base64:同时结合if部分逐个对RC4生成的结果做魔改的Base64运算
  297.                                                 //
  298.                                                 //
  299.                                                 //
  300.   while ( 1 )
  301.   {
  302.     if ( v45_by_app_password_length )
  303.     {                                           // v45 是根据输入密码长度计算,比如密码:12345678;v45=0x48u(即78)
  304.       v34_ret = (_BYTE *)(&dword_0 + 1);        // v34 =1
  305.       v19_v49_256_Rc4_T = (char *)v44_new_base64_length;
  306.       v41 = &byte_24E8;                         // v41指向的char* = 0x20(即空格)+"{9*8ga*l!Tn?@#fj'j$\g;;"
  307.       do
  308.       {
  309.         v29 = (unsigned __int8)v28_new_base64[v27++];
  310.         v42 = (unsigned __int8)*v41++;
  311.         if ( v42 != v29 )
  312.           v34_ret = 0;
  313.       }
  314.       while ( v27 < v44_new_base64_length );    // v44 =0x3Fu
  315.     }
  316.     else
  317.     {
  318.       v34_ret = (_BYTE *)(&dword_0 + 1);
  319.     }
  320.     v28_new_base64 = (_BYTE *)(_stack_chk_guard - v50);
  321.     if ( _stack_chk_guard == v50 )
  322.       break;
  323. LABEL_53:
  324.     v40 = v34_ret[v29];
  325.     v19_v49_256_Rc4_T[2] = 52;                  // 输入密码:12345678,这里是:wk4.
  326. LABEL_43:
  327.     v19_v49_256_Rc4_T[1] = v40;
  328.   }
  329.   return (unsigned __int8)v34_ret;              // sub_784 方法返回只有这么一个位置。我们侧重分析如何让该方法返回真即可
  330. }
复制代码


查阅资料
syang大神的看雪CTF-变形金刚:
http://blog.syang.xyz/2019/04/kanxue-transformer/

HHHso大神[原创] KCTF 2019 Q1 第二题 有的放矢:
https://bbs.pediy.com/thread-250348-1.htm

经典对称加密RC4分析 :
http://ronpa.top/2018/11/27/%E7%BB%8F%E5%85%B8%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86RC4%E5%88%86%E6%9E%90/

用 Java 实现的 Rc4 加密算法(C++):
https://www.cnblogs.com/java-stack/archive/2010/10/25/11952676.html

详解Java中的Base64原理跟z用法:
https://blog.csdn.net/qq_36522306/article/details/80753099


在线工具
ASCII码对照表:
https://tool.oschina.net/commons?type=4
在线进制转换器:
https://tool.oschina.net/hexconvert/


  感受
CTF变形金刚题目设计非常巧妙,通过本例的跟踪分析学到不到逆向与反逆向技术;提升自己对加密算法的理解。

分享是一种美德,牵手是一种生活方式。















回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-9-20 14:33 , Processed in 0.016600 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表