-运行界面:
找flag,用户输入一串字符串点击cheek确认输入是否正确
- 代码分析
判断用户输入字符串是否正确
先用一个a方法加密之后传入native函数ncheck进行判定;也就是在Java层有一次加密,在native层还有一次加密;
Java层的加密,类似一个换了码表的base64加密;
package com.a.easyjni;
public class a {
private static final char[] a;
static {
a.a = 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 String a(byte[] arg10) {
StringBuilder v4 = new StringBuilder();
int v0;
for(v0 = 0; v0 <= arg10.length - 1; v0 += 3) {
byte[] v5 = new byte[4];
int v3 = 0;
byte v2 = 0;
while(v3 <= 2) {
int v7 = arg10.length - 1;
if(v0 + v3 <= v7) {
v5[v3] = (byte)(v2 | (arg10[v0 + v3] & 0xFF) >>> v7 * 2 + 2);
v2 = (byte)(((arg10[v0 + v3] & 0xFF) << (2 - v3) * 2 + 2 & 0xFF) >>> 2);
}
else {
v5[v3] = v2;
v2 = 0x40;
}
++v3;
}
v5[3] = v2;
int v2_1;
for(v2_1 = 0; v2_1 <= 3; ++v2_1) {
if(v5[v2_1] <= 0x3F) {
v4.append(a.a[v5[v2_1]]);
}
else {
v4.append('=');
}
}
}
return v4.toString();
}
}
so层的加密函数Java_com_a_easyjni_MainActivity_ncheck,最后比较的字符串是"MbT3sQgX039i3gAQOoMQFPskB1Bsc7",
这个也就是我们输入的字符串通过2次加密之后的结果;
这里面的加密逻辑比较简单,就是前16位和后16位互换,然后第i位和i+1位互换,i位偶数和0,也就是0、1互换,2、3互换,4、5互换。。。;
前后16位互换之后 : AQOoMQFPskB1Bsc7MbT3sQgX039i3g
然后i和i+1位互换后:QAoOQMPFks1BsB7cbM3TQsXg30i9g3==
signed int __fastcall Java_com_a_easyjni_MainActivity_ncheck(JNIEnv *a1, int a2, int key)
{
int v3; // r8
JNIEnv *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 = key;
v6 = ((*a1)->GetStringUTFChars)(a1, key, 0);
if ( strlen(v6) == 32 )
{
v7 = 0;
do
{
v8 = &s1[v7];
s1[v7] = v6[v7 + 16];
v9 = v6[v7++];
v8[16] = v9; // s1前16位=key后16位
} // s1后16位=key前16位
while ( v7 != 16 );
((*v4)->ReleaseStringUTFChars)(v4, v5, v6);
v10 = 0;
do
{
v12 = __OFSUB__(v10, 30); // v10-30是否溢出
v11 = v10 - 30 < 0;
v16 = s1[v10];
s1[v10] = s1[v10 + 1];
s1[v10 + 1] = v16;
v10 += 2; // s[i]和s[i+1],i为0和偶数,互换位置,就是0和1、2和3、4和5互换;
}
while ( v11 ^ v12 );
v13 = memcmp(s1, "MbT3sQgX039i3g==AQOoMQFPskB1Bsc7", ' '); //这里比较判断,也就是加密字符串是"MbT3sQgX039i3g==AQOoMQFPskB1Bsc7"
result = 0;
if ( !v13 )
result = 1;
}
else
{
((*v4)->ReleaseStringUTFChars)(v4, v5, v6);
result = 0;
}
return result;
}
上面so里面解密出来的结果就是Java层传过去的值,也就是第4步得到的解密值就是第3步的加密函数的返回值:QAoOQMPFks1BsB7cbM3TQsXg30i9g3==
第3步前面也说过了就是一个换了码表的base64加密,拿我们的返回值解密之后就得到flag:flag{just_ANot#er_@p3}
解密代码:
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
public class a {
public static void main(String[] args) {
char[] a = 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'}; //自定义的码表
String s ="QAoOQMPFks1BsB7cbM3TQsXg30i9g3==";//加密后的base64值
byte[] bytes=s.getBytes();
char[] b= getChars(bytes);
int[] sy = new int[32];
for(int i=0;i<b.length;i++){
for (int j=0;j<a.length;j++){
if(a[j]==b[i]){
sy[i]=j; //得到索引值
//System.out.println(sy[i]);
}
}
}
//每4位为一组进行移位和组合得到3位解密的flag
byte[] bflag = new byte[32];
int n=0;
for(int m=0;m<sy.length-3;m+=4){
bflag[n]=(byte)((sy[m]<<2)|(sy[m+1]>>4)); //bflag[0]
bflag[n+1]=(byte)((sy[m+1]<<4)|(sy[m+2]>>2)); //bflag[1]
bflag[n+2]=(byte)((sy[m+2]<<6)|(sy[m+3])); //bflag[2]
n+=3;
}
String flag =String.valueOf(getChars(bflag));
System.out.println(flag);
}
public static char[] getChars(byte[] bytes) {
Charset cs = Charset.forName("UTF-8");
ByteBuffer bb = ByteBuffer.allocate(bytes.length);
bb.put(bytes);
bb.flip();
CharBuffer cb = cs.decode(bb);
return cb.array();
}
}
又翻到了这个重新解了一遍,补充个代码:
import java.io.ByteArrayOutputStream;
public class test {
public static void main(String[] args) throws Exception {
String v12="MbT3sQgX039i3g==AQOoMQFPskB1Bsc7";
char[] v5=new char[32];//flag
//字符串转char[]
char[] v12c=v12.toCharArray();
//单双位互换
for(int i=0;i<32;i+=2){
char v13=v12c[i];
v12c[i]=v12c[i+1];
v12c[i+1]=v13;
}
System.out.println(String.valueOf(v12c));
//前16位和后16位互换
for (int j=0;j!=16;++j){
v5[j + 16]=v12c[j];
char v8=v12c[16+j];
v5[j]=v8;
}
System.out.println(String.valueOf(v5));
generateDecoder();
System.out.println(new String(Base64Decode(String.valueOf(v5))));
}
private static final char[] Base64ByteToStr = 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'};
//自定义码表base64解码
private static byte[] StrToBase64Byte = new byte[128];
private static final int RANGE = 0xff;
private static byte[] Base64Decode(String val) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();//destination bytes, valid string that we want
byte[] srcBytes = val.getBytes();
byte[] base64bytes = new byte[srcBytes.length];
//get the base64 bytes (the value is -1 or 0 ~ 63)
for(int i = 0; i <= srcBytes.length - 1; i++) {
int ind = (int) srcBytes[i];
base64bytes[i] = StrToBase64Byte[ind];
}
//base64 bytes (4 bytes) to normal bytes (3 bytes)
for(int i = 0; i <= base64bytes.length - 1; i+=4) {
byte[] deBytes = new byte[3];
int delen = 0;// if basebytes[i] = -1, then debytes not append this value
byte tmp ;
for(int k = 0; k <= 2; k++) {
if((i + k + 1) <= base64bytes.length - 1 && base64bytes[i + k + 1] >= 0) {
tmp = (byte) (((int)base64bytes[i + k + 1] & RANGE) >>> (2 + 2 * (2 - (k + 1))));
deBytes[k] = (byte) ((((int) base64bytes[i + k] & RANGE) << (2 + 2 * k) & RANGE) | (int) tmp);
delen++;
}
}
for(int k = 0; k <= delen - 1; k++) {
bos.write((int)deBytes[k]);
}
}
return bos.toByteArray();
}
private static void generateDecoder() throws Exception {
for(int i = 0; i <= StrToBase64Byte.length - 1; i++) {
StrToBase64Byte[i] = -1;
}
for(int i = 0; i <= Base64ByteToStr.length - 1; i++) {
StrToBase64Byte[Base64ByteToStr[i]] = (byte)i;
}
}
}