前言
玩農(nóng)藥的時(shí)候,想著能有透視的輔助就好了家制,在網(wǎng)上找到了一個(gè)輔助應(yīng)用正林,破解開(kāi)發(fā)現(xiàn)是基于AndroLua的腳本應(yīng)用。在逆向的同時(shí)颤殴,發(fā)現(xiàn)了現(xiàn)在火的很多應(yīng)用觅廓,例如O泡果奶等,都使用了同樣的加密方法涵但。
AndroLua還是AndroLua_Pro應(yīng)用的逆向杈绸,其實(shí)大同小異,框架不是逆向的重點(diǎn)矮瘟,基本不會(huì)有人去改瞳脓。主要是lua腳本的逆向,從luac到lua澈侠,可以直接用unluac.jar直接逆向惊窖。但是,加密程序會(huì)對(duì)腳本中的所有字符串進(jìn)行加密混淆槽地,比如字符串斟珊、函數(shù)名、類(lèi)名拳球。比如我遇到的這種审姓,加密過(guò)程為,lua->luac->混淆加密->zlib壓縮->base64編碼祝峻。
原型
zlib壓縮的特征是inflate等相關(guān)方法的應(yīng)用魔吐,base64編碼的特征就是編碼的解碼表扎筒。這里主要關(guān)注的就是字符串加密混淆的方法,也是常見(jiàn)的異或加密酬姆,這種加密方法加密解密的過(guò)程是一樣的嗜桌。
復(fù)原
字符串混淆加密算法復(fù)原
public static byte[] decrypt(byte[] source) {
byte[] result = new byte[source.length];
if (source.length > 0) {
int t = source.length;
result[0] = (byte) (source[0] ^ t);
int temp = source.length + Byte.toUnsignedInt(result[0]);
for (int i = 1; i < source.length; i++) {
t += temp;
result[i] = (byte) (source[i] ^ (t % 255));
}
}
return result;
}
找到了unluac.jar的源碼,在進(jìn)行字符串解碼的時(shí)候轴踱,進(jìn)行解密操作症脂,就可以復(fù)原出真實(shí)的lua腳本。但是在實(shí)際使用的時(shí)候淫僻,對(duì)中文的復(fù)原不太友好诱篷,猜測(cè)可能與UTF-8編碼有關(guān),我在Python和Java上分別對(duì)用串漢字進(jìn)行UTF-8編碼產(chǎn)生的字節(jié)竟然不一樣雳灵。
zlib壓縮復(fù)原
這個(gè)就比較容易了棕所,根據(jù)inflate特征可以知道是zlib壓縮,為求穩(wěn)妥悯辙,找到對(duì)應(yīng)版本的zlib直接調(diào)api就行琳省,這里用的是Python下的zlib。
zlib.decompress(data)
base64解碼復(fù)原
base64的特征還是很明顯的躲撰,就是解碼表针贬,github上搜一下就能搜到需要的源碼。
static inline int b64index(uint8_t c) {
static const int decoding[] = { 62,-1,-1,-1,63,52,53,54,55,56,57,
58,59,60,61,-1,-1,-1,-2,-1,-1,
-1,0,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,-1,-1,-1,
-1,-1,-1,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 };
int decoding_size = sizeof(decoding) / sizeof(decoding[0]);
if (c < 43) {
return -1;
}
c -= 43;
if (c >= decoding_size)
return -1;
return decoding[c];
}
#define SMALL_CHUNK 256
unsigned char *decode(const unsigned char *buff, size_t size) {
int decode_sz = (size + 3) / 4 * 3;
unsigned char *result = (unsigned char *)malloc(size);
int i, j;
int output = 0;
for (i = 0; i<size;) {
int padding = 0;
int c[4];
for (j = 0; j<4;) {
if (i >= size) {
cout << "ERROR" << endl;
/*return luaL_error(L, "Invalid base64 text");*/
return result;
}
if (i == 0) {
c[j] = 7;
}
else {
c[j] = b64index(buff[i]);
}
if (c[j] == -1) {
++i;
continue;
}
if (c[j] == -2) {
++padding;
}
++i;
++j;
}
uint32_t v;
switch (padding) {
case 0:
v = (unsigned)c[0] << 18 | c[1] << 12 | c[2] << 6 | c[3];
result[output] = v >> 16;
result[output + 1] = (v >> 8) & 0xff;
result[output + 2] = v & 0xff;
output += 3;
break;
case 1:
if (c[3] != -2 || (c[2] & 3) != 0) {
cout << "ERROR" << endl;
//return luaL_error(L, "Invalid base64 text");
return result;
}
v = (unsigned)c[0] << 10 | c[1] << 4 | c[2] >> 2;
result[output] = v >> 8;
result[output + 1] = v & 0xff;
output += 2;
break;
case 2:
if (c[3] != -2 || c[2] != -2 || (c[1] & 0xf) != 0) {
cout << "ERROR" << endl;
//return luaL_error(L, "Invalid base64 text");
return result;
}
v = (unsigned)c[0] << 2 | c[1] >> 4;
result[output] = v;
++output;
break;
default:
cout << "ERROR" << endl;
//return luaL_error(L, "Invalid base64 text");
return result;
}
}
return result;
}
至此拢蛋,基本上現(xiàn)在常見(jiàn)的lua腳本加密的逆向過(guò)程就完成了桦他。
總結(jié)
最后接一個(gè)貼士,
農(nóng)藥的透視輔助谆棱,其實(shí)就是讀取內(nèi)存中的一段數(shù)據(jù)快压,然后傳遞到主程序,繪制出來(lái)垃瞧,核心點(diǎn)在于知道保存這段關(guān)鍵數(shù)據(jù)的內(nèi)存地址蔫劣。但是,隨著Android版本不斷更新个从,權(quán)限管理也越來(lái)越嚴(yán)格脉幢,哪怕是root權(quán)限,android應(yīng)用也不能訪問(wèn)其他應(yīng)用的數(shù)據(jù)∴氯瘢現(xiàn)在常見(jiàn)的折中方法鸵隧,寫(xiě)一段c程序,通過(guò)root權(quán)限意推,調(diào)用su指令調(diào)用這個(gè)c文件,將其他應(yīng)用的內(nèi)存數(shù)據(jù)讀取保存到當(dāng)前應(yīng)用可以訪問(wèn)的文件珊蟀,然后讓輔助應(yīng)用跟進(jìn)更新菊值。