本文已在看雪上发布过
感觉做完之后能对 Android 逆向有更进一步的理解,特地码一下,也拜读了几位大佬的文章。
1、看一下androidmanifest.xml
就一个活动进去看看
2、Mainactivity
看起来只要把用户名跟密码 作为 颠倒的字符串就行
然而并不是,再仔细观察就能发现
开始以为是android 原生的,然而是改了一个相似的名字不仔细看,很容易忽略,这个操作挺骚的
懒得截图,直接用了某大佬的图
3、AppCompaitActivity
真正的校验逻辑在 AppCompaitActivity 这个类的 public native boolean eq(String str); 函数内
这个函数是一个 native 函数,那么具体的校验逻辑必然在 oo000oo 对应的 so 文件中
4、发现so文件中并没有eq该函数,所以判断在JNI_LOAD或者在init_array里面做了手脚。
5、跟踪.init_array
init_array函数执行了datadiv_decode5009363700628197108字符串解密函数
650f909c-7217-3647-9331-c82df8b98e98
!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';
android/support/v7/app/AppCompiatActivity
Eq (Ljava/lang/String;)Z
总结:
#解码36长度字符串byte_4020:
byte_4020 =650f909c-7217-3647-9331-c82df8b98e98+0x00(结束符)
#解码Base64为的64+1个编码字符byte_4050=
base64Chars = byte_4050 =!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';+0x00(结束符)
#app伪装java类的名称byte_40A0:
className_Sign= android/support/v7/app/AppCompiatActivity +0x00(结束符)
6、JNI_OnLoad
7、动态注册
8、关键函数
<code>
111
</code>
<pre>2222</pre>
、、、
11111
、、、
'''
222
'''
```
holod
```
app_password = (char*)(*(int(**)(void))(*(_DWORD *)a1 + 676))();
rc4_key_len = strlen(rc4_raw_key); // rc4_raw_key = 650f909c-7217-3647-9331-c82df8b98e98
rc4_key3 = (unsigned __int8*)malloc(rc4_key_len);// rc4_raw_key 被删除"-"符号,再经过特殊处理,同时有添加回新的“-”符号
// 新的结果:89e89b8f-d28c-1339-7463-7127c909f056
rc4_key2 = malloc(rc4_key_len); // rc4_raw_key的内容删除“-” :650f909c721736479331c82df8b98e98
rc4_key4 = malloc(rc4_key_len);
_aeabi_memclr(rc4_key3, rc4_key_len); // RC4密钥生成
_aeabi_memclr(rc4_key2, rc4_key_len); // aeabi_memclr(memset的地址和长度是常量)
_aeabi_memclr(rc4_key4, rc4_key_len);
if( rc4_key_len ) // key长度大于0
{
rc4_key2_len = 0;
v6 = rc4_key_len;
v7 = rc4_raw_key;
do
{
v8 = (unsigned __int8)*v7++;
if( v8 != 45 ) // 45(-)减号/破折号
rc4_key2[rc4_key2_len++] = v8;
--v6;
}
while( v6 ); // 删除(rc4_raw_key中的"-") =650f909c721736479331c82df8b98e98
if( rc4_key2_len >= 1 )
{
rc4_key2_pos = rc4_key2_len - 1;
v10 = -8;
v11 = 0;
rc4_key3_len = 0;
do
{
if( (v11 | (v10 >> 2)) > 3 ) // // v10>>2 = -2?后第一次=0x3FFFFFFE
{
rc4_key3_pos = rc4_key3_len;
}
else
{
rc4_key3_pos = rc4_key3_len + 1;
rc4_key3[rc4_key3_len] = 45;
}
v14 = rc4_key2[rc4_key2_pos--];
v11 += 0x40000000;
rc4_key3[rc4_key3_pos] = v14;
++v10;
rc4_key3_len = rc4_key3_pos + 1;
}
while( rc4_key2_pos != -1 );
if( rc4_key3_pos >= 0 )
{
v15 = rc4_key4;
while( 1 )
{
v16 = (_BYTE *)*rc4_key3;
if( (unsigned __int8)((_BYTE)v16 - 97) <= 5u )
break;
if( (unsigned __int8)((_BYTE)v16 - 48) <= 9u )
{
v16 = (char*)&unk_23DE + (_DWORD)v16 - 48;
gotoLABEL_18;
}
LABEL_19:
*v15++ = (_BYTE)v16;
--rc4_key3_len;
++rc4_key3;
if( !rc4_key3_len )
gotoLABEL_20;
}
v16 = (char*)&unk_23D8 + (_DWORD)v16 - 97;
LABEL_18:
LOBYTE(v16) = *v16;
gotoLABEL_19;
}
}
}
LABEL_20: // sbox 生成
_aeabi_memcpy8(sbox, &unk_23E8, 256);
Rc4_T = tbox;
tbox_pos = 0;
do
{
sub_D20(tbox_pos, rc4_key_len); // 需要跟进去看看
tbox[tbox_pos++] = rc4_key4[v19];
}
while( tbox_pos != 256 );
ix = (unsigned __int8)(tbox[0] - 41);
sbox[0] = sbox[ix];
sbox[ix] = -41; // d7
v21 = 1;
do
{
v22 = (unsigned __int8)sbox[v21];
ix = (ix + (unsigned __int8)tbox[v21] + v22) % 256;
sbox[v21++] = sbox[ix];
sbox[ix] = v22;
}
while( v21 != 256 ); // sbox初始化
app_password_length = strlen(app_password); // 就是解密数组密码的字符
app_password_length_loc = app_password_length;
new_base64_index = (unsigned __int8)rc4_key4[3];
pwd_b64_bitlen = 8 * (3 - -3 * (app_password_length / 3));
new_base64_length = new_base64_index + pwd_b64_bitlen / 6;
new_base64 = malloc(new_base64_length + 1); // 分配新的base64需要使用的字节。
if( app_password_length_loc ) // app输入的密码长度
{
txi = 0;
pwd_idx = 0; // 循环体index
txj = 0;
v44 = new_base64_index;
do
{
txi = (txi + 1) % 256; // RC4 算法的产生密钥流循环体开始
tx = (unsigned __int8)sbox[txi];
txj = (txj + tx) % 256;
sbox[txi] = sbox[txj];
sbox[txj] = tx;
Rc4_T = (char*)(unsigned __int8)sbox[txi];
needTodoBase64 = sbox[(unsigned __int8)(tx + (_BYTE)Rc4_T)] ^ app_password[pwd_idx];// Base64内嵌在RC4的内部
// 先线程一个RC4加密后的字符
// 后面用这个字符进行Base64的解码操作
// Base64魔改的算法:
// 1:Base64 字典被替换
// 2:Base最后以为“=”会被替换成“;”
// 3: 对特定字符进行异或操作.每4个字符中第0个与0x6异或,第0个与0xF异或
// switch (i%4){
// case 0:
// base64char = (char) (iAscii ^0x07);
// break;
// case 2:
// base64char = (char) (iAscii ^0xF);
// break;
// default:
// base64char =encodeChars[i];
// }
if( pwd_idx && (v27 = 2863311531u * (unsigned __int64)pwd_idx >> 32, v37 = 3 * (pwd_idx / 3), v37 != pwd_idx) )// 逗号运算符是指在C语言中,多个表达式可以用逗号分开,
// 其中用逗号分开的表达式的值分别结算,但整个表达式的值是最后一个表达式的值。
//
// 影响结果:
// false:
// 其实就是index/3(取摸)!=0
//
// true:
// index/3 =0
// v37:index可以是3的最大倍数,0,3,6,9,12...
// index=3,则v37=3;index =6,则v37=6;index=7,则v37=6
{
v31 = pwd_idx == 1; // 是否是appPassword[1]字符
if( pwd_idx != 1 )
v31 = v37 + 1 == pwd_idx;
if( v31 ) // 判断index%3=1的情况 ,即1,4,7,10
{
retn = base64_table; // v34第一次=0x33(51);
// base64_table:存放的是base64的table字典字符64+1个
// base64_table:(字符长度是64+1):!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\';
new_base64[v44 + pwd_idx] = base64_table[(unsigned __int8)new_base64[v44 + pwd_idx] | ((unsigned int)needTodoBase64 >> 4)];
Rc4_T = &new_base64[v44 + pwd_idx];
v27 = 4 * needTodoBase64 & 0x3C; // 0x3C =60 ,即111100二进制
Rc4_T[1] = v27;
if( pwd_idx + 1 >= app_password_length_loc )
gotoLABEL_53; // 跳转到程序的倒数第2个Lable
}
else
{
v33 = pwd_idx == 2; // index%3=2的情况 2,5,8,11....
if( pwd_idx != 2 )
v33 = v37 + 2 == pwd_idx;
if( v33 )
{
Rc4_T = (char*)(needTodoBase64 & 0xC0);// 0xC0 =192即11000000二进制,保留最高2bit
v34 = v44++ + pwd_idx;
new_base64[v34] = base64_table[(unsigned __int8)new_base64[v34] | ((unsigned int)Rc4_T >> 6)] ^ 0xF;// 0xf=15 ,即二进制1111
// 这里异或好像是对Base64模拟的多余操作
v27 = (int)&new_base64[v34];
*(_BYTE *)(v27 + 1) = base64_table[needTodoBase64 & 0x3F];// 0x3f=63 即111111二进制
}
}
}
else
{
new_base64[v44 + pwd_idx] = base64_table[(unsigned int)needTodoBase64 >> 2] ^ 7;// 7的二进制四111,这里对base64进行了魔改操作
Rc4_T = &new_base64[v44 + pwd_idx];
v27 = 16 * needTodoBase64 & 0x30; // 0x30=48,即二进制110000bit
Rc4_T[1] = v27;
if( pwd_idx + 1 >= app_password_length_loc )// 最后一位
{
v38 = base64_table[v27];
*((_WORD *)Rc4_T + 1) = 15163;
gotoLABEL_43;
}
}
++pwd_idx;
} // end for if ( v26_app_password_length ) 内部的do
while( pwd_idx < app_password_length_loc );
} // end if ( v26_strText_length ) 输入密码lenght>0
// 这是时RC4-04部分的算法“产生密钥流”
// v28_new_base64:同时结合if部分逐个对RC4生成的结果做魔改的Base64运算
while( 1 )
{
if( pwd_b64_bitlen )
{ // v45 是根据输入密码长度计算,比如密码:12345678;v45=0x48u(即78)
retn = (_BYTE *)(&dword_0 + 1); // retn =1
Rc4_T = (char*)new_base64_length;
v39 = &byte_24E8; // v41指向的char* = 0x20(即空格)+"{9*8ga*l!Tn?@#fj'j$\g;;"
do
{
v27 = (unsigned __int8)new_base64[new_base64_index++];
v40 = (unsigned __int8)*v39++;
if( v40 != v27 )
retn = 0;
}
while( new_base64_index < new_base64_length );// v44 =0x3Fu
}
else
{
retn = (_BYTE *)(&dword_0 + 1);
}
new_base64 = (_BYTE *)(_stack_chk_guard - v48);
if( _stack_chk_guard == v48 )
break;
LABEL_53:
v38 = retn[v27];
Rc4_T[2] = 52; // 输入密码:12345678,这里是:wk4.
LABEL_43:
Rc4_T[1] = v38;
}
return(unsigned __int8)retn; // sub_784 方法返回只有这么一个位置。我们侧重分析如何让该方法返回真即可
</code>
接下来我分段分析,我把每一段都分别用python写了一个单独的函数
8.1、RC4密钥去掉-
1
2
3
4
5
6
7
8
9
# RC4密钥去掉-
# return 650f909c721736479331c82df8b98e98
defgetRc4Key2():
rc4key2 =""
fori inrange(len(rc4key)):
value =rc4key[i]
if( ord(value) !=0x2d): # 字符串 转 ascii码
rc4key2 +=value
returnrc4key2
8.2、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# return 89e89b8f-d28c-1339-7463-7127c909f056
defgetRc4Key3(key):
keylen =len(key)
rc4key2value1 =0xFFFFFFF8
rc4key2value2 =0x0
rc4key3 =""
whilekeylen > 0:
temp =rc4key2value1 >> 2
temp2 =rc4key2value2 | temp
iftemp2 <=3:
rc4key3 +=chr(0x2d)# ascii码 转 字符串
keylen -=1
rc4key3 +=key[keylen]
rc4key2value2 +=0x40000000
rc4key2value2 =fixedint.UInt32(rc4key2value2)
rc4key2value1 +=1
rc4key2value1 =fixedint.UInt32(rc4key2value1)
returnrc4key3
8.3、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 算法参数01:unk_23DE(10位长度) =2409715836
# 算法参数02:unk_23D8(16位长度)=dbeafc2409715836
# return 36f36b3c-a03e-4996-8759-8408e626c215
defgetRc4Key4(key):
keylen =len(key)
keypos =0
retnkey =""
unk_23DE ="2409715836"
unk_23D8 ="dbeafc2409715836"
whilekeylen > 0:
value =key[keypos]
valueascii =ord(value)
value97 =valueascii -97
value48 =valueascii -48
if(value97 <=5) and(value97 >=0):
value =unk_23D8[value97]
elif(value48 <=9) and(value48 >=0):
value =unk_23DE[value48]
retnkey +=value
keylen -=1
keypos +=1
returnretnkey
8.4、根据密钥生成临时的数据key
1
2
3
4
5
6
7
8
9
10
11
12
# 根据密钥生成临时的数据key
# 计算RC4 的临时256自己T向量,公式:iK[i]=(byte)aKey.charAt((i % aKey.length()));
defgetRc4Key5(key):
tbox =""
pos =0
keylen =len(key)
whilepos < 256:
tbox +=key[pos %keylen]
pos +=1
returntbox
8.5、sbox初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
defgetSboxK(key):
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]
ix =ord(key[0]) -0x29
Sbox[0] =Sbox[ix]
Sbox[ix] =0xd7
sboxpos =1
whilesboxpos < 256:
value =Sbox[sboxpos]
ix =(ix +ord(key[sboxpos]) +value)%256
Sbox[sboxpos] =Sbox[ix]
Sbox[ix] =value
sboxpos +=1
returnSbox
8.6、接下来就是 RC4 + base64混合加密的地方了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
defgetNewBase64(password,tbox,rc4key4):
password_length =len(password)
b64_salt_size =ord(rc4key4[3])
# 空数组
new_base64 =[]
ifpassword_length <=0:
return
txi =0
pwd_idx =0
txj =0
new_base64_tmpValue =0
whilepwd_idx < password_length:
txi =(txi +1) %256
tx =tbox[txi]
txj =(txj +tx) %256
tbox[txi] =tbox[txj]
tbox[txj] =tx
Rc4_T =tbox[txi]
tboxIndex =byteToInteger(tx +Rc4_T)
needTodoBase64 =byteToInteger(tbox[tboxIndex] ^ ord(password[pwd_idx]))
pwdtem =pwd_idx %3
ifpwdtem ==0:
base64table_index =needTodoBase64 >> 2
base64table_value =ord(base64table[base64table_index])
base64table_tmp =base64table_value ^ 7
new_base64.append(base64table_tmp)
needbaser64_tmp =16*needTodoBase64
new_base64_tmpValue =needbaser64_tmp & 0x30
elifpwdtem ==1:
base64table_index_temp =needTodoBase64 >> 4
base64table_index =base64table_index_temp +new_base64_tmpValue
new_base64.append(ord(base64table[base64table_index]))
needbaser64_tmp =needTodoBase64 << 2
new_base64_tmpValue =needbaser64_tmp & 0x3C
pwd_idx_tem =pwd_idx +1
ifpwd_idx_tem ==password_length:
new_base64.append(ord(base64table[new_base64_tmpValue]))
new_base64.append(0x34)
elifpwdtem ==2:
needbaser64_tmp2 =needTodoBase64 & 0xC0
b64_salt_size =b64_salt_size +1
base64table_index_temp =needbaser64_tmp2 >> 6
base64table_index =new_base64_tmpValue +base64table_index_temp
base64table_value =ord(base64table[base64table_index])
base64table_tmp =base64table_value ^ 0xf
new_base64.append(base64table_tmp)
needbaser64_tmp2 =needTodoBase64 & 0x3F
new_base64_tmpValue =ord(base64table[needbaser64_tmp2])
new_base64.append(new_base64_tmpValue)
pwd_idx =pwd_idx +1
returnnew_base64
9、分析文档及代码地址:https://github.com/dadaoshizhong/rc4-base64-/upload
10、参考
https://bbs.pediy.com/thread-262472.htm
https://bbs.pediy.com/thread-250348-1.htm
http://blog.syang.xyz/2019/04/kanxue-transformer/
https://bbs.pediy.com/thread-250413.htm
https://xz.aliyun.com/t/4614