Prolong
1 | .init_array:00003E78 ; ELF Initialization Function Table |
程序在.init_array中重新初始化了一些字段,包括生成RC4的初始密钥K、状态向量S,下面说下K的生成过程:
1) so中原始存储了长度为36数据:
0x93,0x90,0x95,0xC3,0x9C,0x95,0x9C,0xC6,0x88,0x92,0x97,0x94,
0x92,0x88,0x96,0x93,0x91,0x92,0x88,0x9C,0x96,0x96,0x94,0x88
0xC6,0x9D,0x97,0xC1,0xC3,0x9D,0xC7,0x9C,0x9D,0xC0,0x9C,0x9D
2) 经过.init_array重新初始化变成了:
650f909c-7217-3647-9331-c82df8b98e98
3) 经过一系列移位变成了:
89e89b8f-d28c-1339-7463-7127c909f056
4) 然后经过一个映射表abcdef0123456789=>dbeafc2409715836
映射为了最终的K36f36b3c-a03e-4996-8759-8408e626c215
状态向量也进行了重新初始化,下面主要分析:
1、变形的RC4算法
2、变形的base64算法
3、给出解密脚本
一、变形的RC4算法
RC4包含两个向量,状态向量S[256],向量T[256],T是由用户自定义初始密钥K轮转填充到256字节,算法主要包含两部分内容
1、用T置乱重排S
2、用T和明文生成密文
1 | 状态向量S: |
1、用T置乱重排S
1 | .text:B3E408BA loc_B3E408BA ; CODE XREF: sub_B3E40784+15E↓j |
翻译成C语言算法如下:
1 | i = 0; |
T[0] - 0x29就是(T[0] + S[0])256,优化一下就是:
1 | j = 0; |
2、用状态向量T和明文一次一密生成密文
1 | .text:B3E409A8 loc_B3E409A8 ; CODE XREF: sub_B3E40784+1B0↑j |
RC4部分对应的C语言算法如下:
1 | i = (i + 1)%256; |
3、变形的RC4算法总结
从上面分析,可以知道该算法与常规RC4算法的不同:
1、使用自定义的初始密钥K
2、使用自定义的状态向量S
二、变形的base64算法
base64算法中,3个字符为一组被编码成4个base64字符,假设待编码字符索引为index,某一组的三个字符为x0、x1、x2,该组base64编码结果对应的字符为y0、y1、y2、y3。
这里需要判断当前处理的index是这一组中的第几个字符,条件为:
1 | .text:B3E409AC CMP R2, #0 |
1 | .text:B3E409F8 MOV R1, #0xAAAAAAAB |
上面为两个条件,整合到一起就是
1 | if(index!=0 && 3*(index/3)!=index) |
这里就判断了当前index,处于小组的位置,是不是x0,若是x0,就去执行loc_B3E40A0E;若是x1、x2就去执行loc_B3E40936
但是执行loc_B3E40936后,还需要判断当前index处于的位置是x1还是x2,就有了下面的判断
1 | .text:B3E40936 CMP R2, #1 ; 比较index和1 |
1 | if(3*(index/3)+1 != index) |
若3*(index/3)+1 != index,则说明当前index处于x2的位置,否则说明当前index处于x1的位置
1、处理第1个字符,得到第1个base64字符
1 | .text:B3E40A0E loc_B3E40A0E ; 开始处理x0 |
初始密钥 K = 36f36b3c-a03e-4996-8759-8408e626c215
该部分算法总结:
1、取RC4加密结果C的低8位,然后取该值的高6位作为base64字符表索引
2、根据字符索引,查字符表,得到第一个base64字符,设为y0’
3、y0 = y0’ ^ 7
4、N = K[3]
5、将b写入buf[ N + index ]
6、将低2位写入buf[ N + index +1 ]
这里比常规的base64编码算法多了第三步
指令总结:
1、UXTB指令,无符号扩展到32位,即高24位清零 http://www.keil.com/support/man/docs/armasm/armasm_dom1361289924987.htm
2、取一个字符A的低2位,将其作为下一个字符索引的高2位,保存到B中 B = (A<<4) & 00110000b
3、CMP R1,R2 BCS label
,无符号R1<R2,则跳转label
4、CMP R1,R2 BCC label
,无符号R1>=R2,则跳转label
1 | else |
2、处理第2个字符,得到第2个base64字符
下面是处理x1的过程
1 | .text:B3E40936 loc_B3E40936 ; CODE XREF: sub_B3E40784+288↓j |
该部分算法总结:
1、取x0低2位与x2的高4位,组成新的6位数值,作为base64字符表索引
2、根据字符索引,查字符表,得到第二个base64字符,设为y1
3、N = K[3]
4、将y1写入buf[ N + index ]
5、将x1低4位写入buf[ N + index +1 ]
指令总结:
1、IT(If-Then)指令:
1 | .text:B3E40936 loc_B3E40936 ; CODE XREF: sub_B3E40784+288↓j |
IT指令用于根据特定条件来执行紧随其后的1~4条指令,其格式为:IT{x{y{z}}} {cond}
。其中x、y、z分别是执行第二、三、四条指令的条件,可取的值为T(then)或E(else),若为T,则执行cond对应的指令;若为E,则执行cond相反条件的指令。而cond对应执行第一条指令的条件。参考
2、 BCC
1 | CMP A,B |
A<B,则跳转到label
3、处理第3个字符,得到第3、4个base64字符
1 | .text:B415E96A loc_B415E96A ; CODE XREF: sub_B415E784+1BC↑j |
这里再次判断了index在组的位置(感觉是多余的
根据注释可以清楚的看到,生成了第3、4个base64字符,其中和常规base64算法不同的是,第3个base64字符与0xF进行了异或处理
到此第一组3个字符,生成了4个base64字符。
4、strlen(s) % 3 = 1时的处理
当base64处理的字符串长度不能被3整除,会有其他的处理
常规的base64算法的处理方式是直接补零查表
而这个变形的base64算法有点不同
我们从当剩余字符为x0时的处理方式开始分析,从上面的跳转分析,可以知道该段代码在loc_00000A3C
1 | .text:B4052A3C loc_B4052A3C ; CODE XREF: sub_B4052784+2AE↑j |
用常规base64算法生成y0、y1后,不同的是,该变形算法在后面追加了0x3B3B,为“;;”,想当于base64算法中的==
5、strlen(s) % 3 = 2时的处理
当剩余字符为x1时的处理方式的分析,从上面的跳转分析,可以知道该段代码在loc_00000A82
1 | .text:B3A1EA82 loc_B3A1EA82 |
用常规base64算法生成y0、y1、y2后,不同的是,该变形算法在后面追加了0x34
至此变形base64算法分析完成
6、变形base64算法总结
设一组待编码的字符长度为L,生成编码字符为y0、y1、y2、y3
与传统base64算法不同的是:
1、字符表变成 !:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\‘;”
2、y0 = y0 ^ 0x7
3、y2 = y2 ^ 0xF
4、若L % 3 = 2,y3 = 0x34
三、解密代码
最后处理的结果会与 {9*8ga*l!Tn?@#fj'j",0x24,"\g;;
对比
由以上分析,我们就可以写出解密代码
1 | #!/usr/bin/python |