打開是一道安卓逆向題,珍藏的APKtool不好使,詢問大佬,曰:JEB。
打開后發(fā)現(xiàn)MainActivity中的oncreate中,注冊(cè)了onclick事件,并在回掉中進(jìn)行校驗(yàn):

看似就是簡(jiǎn)單的用戶名和密碼倒序的校驗(yàn),但是其實(shí)這個(gè)校驗(yàn)是假的
MainActivity繼承的不是系統(tǒng)自帶的AppCompatActivity類,而是自己編寫的AppCompiatActivity類,,,,一個(gè)i的差別。。并且這個(gè)類放在了系統(tǒng)包目錄里面。。。
AppCompiatActivity類中有onstart,注冊(cè)了另一個(gè)onclick回掉,由于安卓的生命周期,onstart會(huì)后于oncreate,從而覆蓋掉之前注冊(cè)的毀掉。。

真正的校驗(yàn)代碼:

核心校驗(yàn)位于oo000oo.so在native層導(dǎo)出的eq函數(shù),于是IDA打開so,本題正式開始。
打開發(fā)現(xiàn)并沒有eq函數(shù)導(dǎo)出,百度得到應(yīng)該是動(dòng)態(tài)導(dǎo)出的。
根據(jù)so文件的加載流程應(yīng)該是先加載init_array,然后是JNI_OnLoad
打開發(fā)現(xiàn)so的init_array中有東西,會(huì)先執(zhí)行.datadiv_decode5009363700628197108,異或解密一些數(shù)據(jù)。
然后JNI_OnLoad中完成了eq函數(shù)的導(dǎo)出

PS:為了方便閱讀代碼,可以把v4、v5變量類型改為JNIEnv*。
分析得知eq函數(shù)就是sub_784(實(shí)際上JNI_OnLoad中注冊(cè)的地址是0x785,有點(diǎn)奇怪。。經(jīng)過測(cè)試,個(gè)人猜想:0x784這個(gè)字節(jié)代表操作數(shù),0x785這個(gè)字節(jié)代表指令,也就是說arm是把指令放在操作數(shù)的后面的。。arm中匯編語(yǔ)句的地址應(yīng)該是按指令的地址算的,,而ida是按照匯編語(yǔ)句的第一個(gè)字節(jié)的地址算的)
int __fastcall sub_784(JNIEnv *a1)
{
size_t v1; // r10
unsigned __int8 *v2; // r6
_BYTE *v3; // r8
_BYTE *v4; // r11
int v5; // r0
size_t v6; // r2
char *v7; // r1
int v8; // r3
int v9; // r1
unsigned int v10; // r2
int v11; // r3
int v12; // r0
int v13; // r4
unsigned __int8 v14; // r0
_BYTE *v15; // r3
_BYTE *v16; // r5
char *v17; // r4
int v18; // r5
int v19; // r1
int v20; // r0
signed int v21; // r1
int v22; // r2
size_t v23; // r0
unsigned int v24; // r8
unsigned int v25; // r5
_BYTE *v26; // r0
int v27; // r3
int v28; // r10
unsigned int v29; // r2
int v30; // r12
bool v31; // zf
_BYTE *v32; // r1
bool v33; // zf
int v34; // r3
int v35; // r1
unsigned __int8 v36; // r11
unsigned int v37; // lr
char v38; // r1
char *v39; // r2
int v40; // t1
unsigned int v42; // [sp+4h] [bp-234h]
unsigned int v43; // [sp+8h] [bp-230h]
unsigned int v44; // [sp+10h] [bp-228h]
char *s; // [sp+14h] [bp-224h]
char v46[256]; // [sp+18h] [bp-220h]
char v47[256]; // [sp+118h] [bp-120h]
int v48; // [sp+218h] [bp-20h]
s = (char *)((int (*)(void))(*a1)->GetStringUTFChars)();
v1 = strlen(byte_4020);
v2 = (unsigned __int8 *)malloc(v1);
v3 = malloc(v1);
v4 = malloc(v1);
_aeabi_memclr(v2, v1);
_aeabi_memclr(v3, v1);
_aeabi_memclr(v4, v1);
if ( v1 )
{
v5 = 0;
v6 = v1;
v7 = byte_4020;//byte_4020手工解密就是650f909c-7217-3647-9331-c82df8b98e98
do
{
v8 = (unsigned __int8)*v7++;
if ( v8 != 45 )
v3[v5++] = v8;
--v6;
}
while ( v6 );//去掉‘-’
if ( v5 >= 1 )
{
v9 = v5 - 1;
v10 = -8;
v11 = 0;
v12 = 0;
do
{
if ( (v11 | (v10 >> 2)) > 3 )
{
v13 = v12;
}
else
{
v13 = v12 + 1;
v2[v12] = 45;
}
v14 = v3[v9--];
v11 += 0x40000000;
v2[v13] = v14;
++v10;
v12 = v13 + 1;
}
while ( v9 != -1 );//倒敘輸出,并按照一定的規(guī)律加上‘-’
if ( v13 >= 0 )
{
v15 = v4;
while ( 1 )
{
v16 = (_BYTE *)*v2;
if ( (unsigned __int8)((_BYTE)v16 - 97) <= 5u )
break;
if ( (unsigned __int8)((_BYTE)v16 - 48) <= 9u )
{
v16 = (char *)&unk_23DE + (_DWORD)v16 - 48;
goto LABEL_18;
}
LABEL_19:
*v15++ = (_BYTE)v16;
--v12;
++v2;
if ( !v12 )
goto LABEL_20;
}
v16 = (char *)&unk_23D8 + (_DWORD)v16 - 97;
LABEL_18:
LOBYTE(v16) = *v16;
goto LABEL_19;
}
}
}//單表替換的加密
LABEL_20:
_aeabi_memcpy8(v46, &unk_23E8, 256);
v17 = v47;
v18 = 0;
do
{
sub_D20(v18, v1);
v47[v18++] = v4[v19];
}
while ( v18 != 256 );
v20 = (unsigned __int8)(v47[0] - 41);
v46[0] = v46[v20];
v46[v20] = -41;
v21 = 1;
do
{
v22 = (unsigned __int8)v46[v21];
v20 = (v20 + (unsigned __int8)v47[v21] + v22) % 256;
v46[v21++] = v46[v20];
v46[v20] = v22;
}
while ( v21 != 256 );
v23 = strlen(s);
v24 = v23;
v25 = (unsigned __int8)v4[3];
v43 = 8 * (3 - -3 * (v23 / 3));
v42 = v25 + v43 / 6;
v26 = malloc(v42 + 1);
if ( v24 )
{
v28 = 0;
v29 = 0;
v30 = 0;
v44 = v25;
do
{
v28 = (v28 + 1) % 256;
v35 = (unsigned __int8)v46[v28];
v30 = (v30 + v35) % 256;
v46[v28] = v46[v30];
v46[v30] = v35;
v17 = (char *)(unsigned __int8)v46[v28];
v36 = v46[(unsigned __int8)(v35 + (_BYTE)v17)] ^ s[v29];//動(dòng)態(tài)調(diào)的話只要在這里下端,記錄下每次v46[(unsigned __int8)(v35 + (_BYTE)v17)]的值即可
if ( v29 && (v27 = 0xAAAAAAAB * (unsigned __int64)v29 >> 32, v37 = 3 * (v29 / 3), v37 != v29) )
{
v31 = v29 == 1;
if ( v29 != 1 )
v31 = v37 + 1 == v29;
if ( v31 )
{
v32 = byte_4050;
v26[v44 + v29] = byte_4050[(unsigned __int8)v26[v44 + v29] | ((unsigned int)v36 >> 4)];
v17 = &v26[v44 + v29];
v27 = 4 * v36 & 0x3C;
v17[1] = v27;
if ( v29 + 1 >= v24 )
goto LABEL_53;
}
else
{
v33 = v29 == 2;
if ( v29 != 2 )
v33 = v37 + 2 == v29;
if ( v33 )
{
v17 = (char *)(v36 & 0xC0);
v34 = v44++ + v29;
v26[v34] = byte_4050[(unsigned __int8)v26[v34] | ((unsigned int)v17 >> 6)] ^ 0xF;
v27 = (int)&v26[v34];
*(_BYTE *)(v27 + 1) = byte_4050[v36 & 0x3F];
}
}
}
else
{
v26[v44 + v29] = byte_4050[(unsigned int)v36 >> 2] ^ 7;
v17 = &v26[v44 + v29];
v27 = 16 * v36 & 0x30;
v17[1] = v27;
if ( v29 + 1 >= v24 )
{
v38 = byte_4050[v27];
*((_WORD *)v17 + 1) = 15163;
goto LABEL_43;
}
}
++v29;
}
while ( v29 < v24 );
}
while ( 1 )
{
if ( v43 )
{
v32 = (_BYTE *)(&dword_0 + 1);
v17 = (char *)v42;
v39 = &byte_24E8;
do
{
v27 = (unsigned __int8)v26[v25++];
v40 = (unsigned __int8)*v39++;
if ( v40 != v27 )
v32 = 0;
}
while ( v25 < v42 );
}
else
{
v32 = (_BYTE *)(&dword_0 + 1);
}
v26 = (_BYTE *)(_stack_chk_guard - v48);
if ( _stack_chk_guard == v48 )
break;
LABEL_53:
v38 = v32[v27];
v17[2] = 52;
LABEL_43:
v17[1] = v38;
}
return (unsigned __int8)v32;
}
總的來看,這道題目先是三次自寫的加密,然后是RC4,最后是Base64。。但是和輸入有關(guān)的只在base64那個(gè)地方出現(xiàn)。
思路有兩個(gè):
1.動(dòng)態(tài)調(diào)試到Base64的地方,把之前與輸入無關(guān)的解密的數(shù)據(jù)dump下來,在逆向base64之前的數(shù)據(jù),抑或運(yùn)算即可得到輸入。(調(diào)試需要root的安卓真機(jī))
2.靜態(tài)分析
前面三次自寫的加密,很容易自己解密,第二次復(fù)雜些,我是直接把ida的代碼復(fù)制下來,然后編譯運(yùn)行得到的結(jié)果。。。具體啥邏輯。。沒看明白。。貼上C代碼:
#include <stdio.h>
#include <string.h>
char v3[100]="650f909c721736479331c82df8b98e98";
char v2[100];
int main()
{
int v9=strlen(v3)-1;
unsigned int v10=-8;
int v11=0;
int v12=0;
int v13=0;
int v14=0;
unsigned char v16;
do
{
if ((v11|(v10>>2))>3)
v13=v12;
else{
v13=v12+1;
v2[v12]=45;
}
v14=v3[v9--];
v11+=0x40000000;
v2[v13]=v14;
++v10;
v12=v13+1;
}
while(v9!=-1);
printf(v2);
}
接下來是變形的RC4,其實(shí)就是替換了Sbox[256]這個(gè)表(unk_23E8),密鑰是前面解密的數(shù)據(jù),加密的數(shù)據(jù)是用戶的輸入。加密后在進(jìn)行Base64加密,然后加密后的數(shù)據(jù)與一塊內(nèi)存比較,進(jìn)行check。思路是先解密base64,然后解密rc4即可反推得到正確輸入。
首先解密BASE64,這個(gè)base64改了編碼表,并且補(bǔ)齊的部分不是=而是;表示,并對(duì)下標(biāo)除4余0和2的數(shù)據(jù)再次抑或加密了,上腳本:
import base64
import string
# base 字符集
#base64_charset = string.ascii_uppercase + string.ascii_lowercase + string.digits + '+/'
base64_charset = "!:#$%&()+-*/`~_[]{}?<>,.@^abcdefghijklmnopqrstuvwxyz0123456789\\'"
def encode(origin_bytes):
"""
將bytes類型編碼為base64
:param origin_bytes:需要編碼的bytes
:return:base64字符串
"""
# 將每一位bytes轉(zhuǎn)換為二進(jìn)制字符串
base64_bytes = ['{:0>8}'.format(str(bin(ord(b))).replace('0b', '')) for b in origin_bytes]
resp = ''
nums = len(base64_bytes) // 3
remain = len(base64_bytes) % 3
integral_part = base64_bytes[0:3 * nums]
while integral_part:
# 取三個(gè)字節(jié),以每6比特,轉(zhuǎn)換為4個(gè)整數(shù)
tmp_unit = ''.join(integral_part[0:3])
tmp_unit = [int(tmp_unit[x: x + 6], 2) for x in [0, 6, 12, 18]]
# 取對(duì)應(yīng)base64字符
resp += ''.join([base64_charset[i] for i in tmp_unit])
integral_part = integral_part[3:]
if remain:
# 補(bǔ)齊三個(gè)字節(jié),每個(gè)字節(jié)補(bǔ)充 0000 0000
remain_part = ''.join(base64_bytes[3 * nums:]) + (3 - remain) * '0' * 8
# 取三個(gè)字節(jié),以每6比特,轉(zhuǎn)換為4個(gè)整數(shù)
# 剩余1字節(jié)可構(gòu)造2個(gè)base64字符,補(bǔ)充==;剩余2字節(jié)可構(gòu)造3個(gè)base64字符,補(bǔ)充=
tmp_unit = [int(remain_part[x: x + 6], 2) for x in [0, 6, 12, 18]][:remain + 1]
resp += ''.join([base64_charset[i] for i in tmp_unit]) + (3 - remain) * '='
return resp
def decode(base64_str):
"""
解碼base64字符串
:param base64_str:base64字符串
:return:解碼后的bytearray;若入?yún)⒉皇呛戏╞ase64字符串,返回空bytearray
"""
if not valid_base64_str(base64_str):
pass#return bytearray()
# 對(duì)每一個(gè)base64字符取下標(biāo)索引,并轉(zhuǎn)換為6為二進(jìn)制字符串
base64_bytes = ['{:0>6}'.format(str(bin(base64_charset.index(s))).replace('0b', '')) for s in base64_str if
s != '=']
resp = bytearray()
nums = len(base64_bytes) // 4
remain = len(base64_bytes) % 4
integral_part = base64_bytes[0:4 * nums]
while integral_part:
# 取4個(gè)6位base64字符,作為3個(gè)字節(jié)
tmp_unit = ''.join(integral_part[0:4])
tmp_unit = [int(tmp_unit[x: x + 8], 2) for x in [0, 8, 16]]
for i in tmp_unit:
resp.append(i)
integral_part = integral_part[4:]
if remain:
remain_part = ''.join(base64_bytes[nums * 4:])
tmp_unit = [int(remain_part[i * 8:(i + 1) * 8], 2) for i in range(remain - 1)]
for i in tmp_unit:
resp.append(i)
return resp
def valid_base64_str(b_str):
"""
驗(yàn)證是否為合法base64字符串
:param b_str: 待驗(yàn)證的base64字符串
:return:是否合法
"""
if len(b_str) % 4:
print (123)
return False
for m in b_str:
if m not in base64_charset:
print (m)
return False
return True
data = [0x20,0x7B,0x39,0x2A,0x38,0x67,0x61,0x2A,0x6C,0x21,0x54,0x6E,0x3F,0x40,0x23,0x66,0x6A,0x27,0x6A,0x24,0x5C,0x67,0x3B,0x3B]
for i in range(len(data)):
if i % 4 == 0:
data[i] ^= 0x7
if i % 4 == 2:
data[i] ^= 0xF
if __name__ == '__main__':
data[22]=ord('=')
data[23]=ord('=')
data = ''.join([chr(i) for i in data])
print (data)
print (decode(data))
這個(gè)編碼表在提取的時(shí)候有個(gè)坑,,byte_4050這個(gè)地址有66個(gè)字節(jié)的數(shù)據(jù),直接復(fù)制到python的話因?yàn)橛修D(zhuǎn)義字符啥的,長(zhǎng)度是64,其實(shí)是錯(cuò)的。。記得只復(fù)制64個(gè)字節(jié),,,
接著是RC4:
# -*- coding: utf-8 -*-
import time
class RC4:
def __init__(self, k):
self.Sbox = self.RC4_init(k)
def RC4_init(self,k):
#Sbox = range(256)
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]
T = [ord(k[i % len(k)]) for i in range(256)]
j = 0
for i in range(256):
j = (j + Sbox[i] + T[i]) & 0xFF
Sbox[i], Sbox[j] = Sbox[j], Sbox[i]
return Sbox
def Encrypt(self,m):
c = ""
S = self.Sbox[:]
i = j = 0
for ch in m:
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
t = (S[i] + S[j]) & 0xFF
c += chr(ord(ch) ^ S[t])
return c
def Decrypt(self,m):
c = ""
S = self.Sbox[:]
i = j = 0
for ch in m:
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
t = (S[i] + S[j]) & 0xFF
c += chr(ord(ch) ^ S[t])
return c
if __name__ == "__main__":
rc4 = RC4("36f36b3c-a03e-4996-8759-8408e626c215")
m = rc4.Decrypt("\xfd\x1e\x8aN\t\xca\x90\x03\xe7\xf1\x85\x9f\x9b\xf7\x83>")
print m

把這個(gè)密碼輸入,點(diǎn)登陸會(huì)提示flag{android4-9},,,然而flag并不是這個(gè)!而是之前解密出來的密碼。。。。別問我咋知道的。。大佬說的
總之這道題目在大佬指點(diǎn)下做完了。。后續(xù)希望整理學(xué)習(xí)一些密碼學(xué)的資料。