首先拿到apk,直接反编译,看看逻辑(别问我为什么不跑一下,跑了能有思路?)
两个函数,一个是主函数,另一个估计是加密函数,看看其逻辑,大致意思输入一个数,然后一系类操作后返回一个true就是对了。那么看看他的加密逻辑。
好的不愧是easyJNI,有一部分加密在SO文件里面,这里先不急先看看java代码
大致意思是,输入的代码,进入a函数去跑。恕我直言这个加密代码我不会,但是一看这个码表是人都觉得是base64 ,既然如此,手撸(网上抄的)base64解密,然后改一下码表就好了。
出于好心,这里贴出来代码
package zhiyuanhui.util;
import java.util.List;
import java.util.ArrayList;
public class Base64 {
// private static String base64Code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
private static String base64Code = String.valueOf(new char[]{'i', '5', 'j', 'L', 'W', '7', 'S', '0', 'G', 'X', '6', 'u', 'f', '1', 'c', 'v', '3', 'n', 'y', '4', 'q', '8', 'e', 's', '2', 'Q', '+', 'b', 'd', 'k', 'Y', 'g', 'K', 'O', 'I', 'T', '/', 't', 'A', 'x', 'U', 'r', 'F', 'l', 'V', 'P', 'z', 'h', 'm', 'o', 'w', '9', 'B', 'H', 'C', 'M', 'D', 'p', 'E', 'a', 'J', 'R', 'Z', 'N'});
public static void main(String[] args) {
System.out.println(encode("Base64"));
System.out.println(decode("QmFzZTY0"));
}
public static String encode(String srcData) {
if(srcData == null && srcData.length() == 0) {
return srcData;
}
char[] chArr = srcData.toCharArray();
String asciiBin = null;
StringBuilder asciiBin_all = new StringBuilder();
for(int i= 0; i< chArr.length; i++) {
//将字符转换成ASCII编码再转换成对应二进位
asciiBin = Integer.toBinaryString((int)chArr[i]);
//给不足8位的在高位补0直到补足8位
while(asciiBin.length()< 8) {
asciiBin= "0"+ asciiBin;
}
//最后把所有二进位拼接成一个字串
asciiBin_all.append(asciiBin);
}
//若长度不能被6整除,则在低位补0到能被6整除为止
while(asciiBin_all.length()% 6!= 0) {
asciiBin_all.append("0");
}
String asciiBinStr = asciiBin_all.toString();
//按6个一组拆分成字串数组
List<String> bin6List = new ArrayList<String>();
String temp = null;
while(asciiBinStr.length()/ 6> 0) {
temp = asciiBinStr.substring(0, 6);
asciiBinStr = asciiBinStr.substring(6);
bin6List.add(temp);
}
String[] bin6Str = bin6List.toArray(new String[bin6List.size()]);
int[] index = new int[bin6Str.length];
//确定最终补位长度
int overLen = 0;
if(srcData.length()% 3 != 0) {
overLen = 3- srcData.length()% 3;
}
//设定存放最终编码的容器
char[] code = new char[index.length+ overLen];
for(int i= 0; i< index.length; i++) {
//将二进位转换成十进制数字
index[i] = Integer.parseInt(bin6Str[i], 2);
//Base64 : Value -> Encoding
code[i] = base64Code.charAt(index[i]);
}
switch(overLen) {
case 2:code[code.length- 2] = '=';//不需要break
case 1:code[code.length- 1] = '=';
default:
}
return String.valueOf(code);
}
public static String decode(String srcData) {
//检测元数据中“=”的个数,并将之去除
int counter = 0;
if(srcData.contains("=")) {
counter = 1;
if(srcData.substring(srcData.length()- 2, srcData.length()- 1).equals("=")) {
counter = 2;
}
}
srcData = srcData.replaceAll("=", "");
//将密文根据Base64编码表转换成对应Value,再转换成二进位 ,然后将所有二进位补足6位,最后将所有二进位存进一个字串
char[] srcCh = srcData.toCharArray();
StringBuffer bin6SB = new StringBuffer();
int index;
String bin6Str;
for(int i= 0; i< srcCh.length; i++) {
//获得Base64编码表的Value
index = base64Code.indexOf(srcCh[i]);
//将Value转为二进位
bin6Str = Integer.toBinaryString(index);
//在长度不足6位的二进位的高位上补0直到补足6位,再保存进字串
while(bin6Str.length()< 6) {
bin6Str = "0"+ bin6Str;
}
bin6SB.append(bin6Str);
}
String bin6Str_all = bin6SB.toString();
//如果二进位字串后有多补的0,将之去除
if(counter == 1) {
bin6Str_all = bin6Str_all.substring(0, bin6Str_all.length()- 2);
} else if(counter == 2) {
bin6Str_all = bin6Str_all.substring(0, bin6Str_all.length()- 4);
}
//按8个一组拆分成字串数组
List<String> bin8List = new ArrayList<String>();
String temp;
while(bin6Str_all.length()/ 6> 0) {
temp = bin6Str_all.substring(0, 8);
bin6Str_all = bin6Str_all.substring(8);
bin8List.add(temp);
}
String[] bin8Str = bin8List.toArray(new String[bin8List.size()]);
//将该字串数组的每个元素(即一组二进位)转成十进制数,再强制转换成char类型
char[] ascii = new char[bin8Str.length];
for(int i= 0; i< ascii.length; i++) {
ascii[i] = (char)Integer.parseInt(bin8Str[i], 2);
}
return String.valueOf(ascii);
}
已经有了解码了,接下来看看ncheck函数里面是啥,打开ida 反编译下so文件
得到如下代码
signed int __fastcall Java_com_a_easyjni_MainActivity_ncheck(int a1, int a2, int a3)
{
int v3; // r8
int v4; // r5
int v5; // r8
const char *v6; // r6
int v7; // r0
char *v8; // r2
char v9; // r1
int v10; // r0
bool v11; // nf
unsigned __int8 v12; // vf
int v13; // r1
signed int result; // r0
char s1[32]; // [sp+3h] [bp-35h]
char v16; // [sp+23h] [bp-15h]
int v17; // [sp+28h] [bp-10h]
v17 = v3;
v4 = a1;
v5 = a3;
v6 = (const char *)(*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a3, 0);
if ( strlen(v6) == 32 )
{
v7 = 0;
do
{
v8 = &s1[v7];
s1[v7] = v6[v7 + 16];
v9 = v6[v7++];
v8[16] = v9;
}
while ( v7 != 16 );
(*(void (__fastcall **)(int, int, const char *))(*(_DWORD *)v4 + 680))(v4, v5, v6);
v10 = 0;
do
{
v12 = __OFSUB__(v10, 30);
v11 = v10 - 30 < 0;
v16 = s1[v10];
s1[v10] = s1[v10 + 1];
s1[v10 + 1] = v16;
v10 += 2;
}
while ( v11 ^ v12 );
v13 = memcmp(s1, "MbT3sQgX039i3g==AQOoMQFPskB1Bsc7", 0x20u);
result = 0;
if ( !v13 )
result = 1;
}
else
{
(*(void (__fastcall **)(int, int, const char *))(*(_DWORD *)v4 + 680))(v4, v5, v6);
result = 0;
}
return result;
}
可以的这C语言我看不懂,能看到的画面是说他32位中前16和后16换位置,接下来就蒙了,去看看人家的wp把,大致意思就是换位,然后更改他们的奇偶数的顺序。(OK 白嫖了人家的二段加密)
对MbT3sQgX039i3gAQOoMQFPskB1Bsc7进行解密(懂了逻辑还要看别人代码,我手撸解密),然后得到QAoOQMPFks1BsB7cbM3TQsXg30i9g3
进行解密
flag{just_ANot#er_@p3}