【攻防世界】Moblie系列之easyjni

【攻防世界】Mobile系列之easyjni

程序运行如下图所示,输入字符进行检查

经检测没有进行加壳处理

使用jadx反编译获取其源代码,定位入口点MainActivity()源代码

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
public class MainActivity extends c {
static {
System.loadLibrary("native");
}

/* access modifiers changed from: private */
/* access modifiers changed from: public */
private boolean a(String str) {
try {
return ncheck(new a().a(str.getBytes()));
} catch (Exception e) {
return false;
}
}

private native boolean ncheck(String str);

/* access modifiers changed from: protected */
@Override // android.support.v4.a.aa, android.support.v4.a.i, android.support.v7.app.c
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
/* class com.a.easyjni.MainActivity.AnonymousClass1 */

public void onClick(View view) {
if (MainActivity.this.a((MainActivity) ((EditText) ((MainActivity) this).findViewById(R.id.edit)).getText().toString())) {
Toast.makeText(this, "You are right!", 1).show();
} else {
Toast.makeText(this, "You are wrong! Bye~", 1).show();
}
}
});
}
}

这里就是调用了so层的native文件对输入进行了判断,我们来继续跟踪native.so文件的ncheck()方法,使用IDEA打开反编译获取其源代码

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
bool __fastcall Java_com_a_easyjni_MainActivity_ncheck(int a1, int a2, int a3)
{
const char *v5; // r6
int i; // r0
char *v7; // r2
char v8; // r1
int v9; // r0
bool v10; // cc
_BOOL4 result; // r0
char v12[32]; // [sp+3h] [bp-35h] BYREF
char v13; // [sp+23h] [bp-15h]

v5 = (const char *)(*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a3, 0);
if ( strlen(v5) == 32 )
{
for ( i = 0; i != 16; ++i )
{
v7 = &v12[i];
v12[i] = v5[i + 16];
v8 = v5[i];
v7[16] = v8;
}
(*(void (__fastcall **)(int, int, const char *))(*(_DWORD *)a1 + 680))(a1, a3, v5);
v9 = 0;
do
{
v10 = v9 < 30;
v13 = v12[v9];
v12[v9] = v12[v9 + 1];
v12[v9 + 1] = v13;
v9 += 2;
}
while ( v10 );
result = memcmp(v12, "MbT3sQgX039i3g==AQOoMQFPskB1Bsc7", 0x20u) == 0; //将v12和"MbT3sQgX039i3g==AQOoMQFPskB1Bsc7"前0x20字节作比较
}
else
{
(*(void (__fastcall **)(int, int, const char *))(*(_DWORD *)a1 + 680))(a1, a3, v5);
result = 0;
}
return result;
}

将上面的代码整理下,关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
for ( i = 0; i != 16; ++i )
{
//v7 = &v12[i];
v12[i] = key_chars[i + 16];
tmp = key_chars[i];
v12[i+16] = tmp;
}
(*nev)->ReleaseStringUTFChars(nev, key, key_chars);

i = 0;
do
{
v10 = i < 30;
tmp = v12[i];
v12[i] = v12[i + 1];
v12[i + 1] = tmp;
i += 2;
}
while ( v10 );
return memcmp(v12, "MbT3sQgX039i3g==AQOoMQFPskB1Bsc7", 0x20u) == 0;

就是先将前16位与后16位相交换,然后两两交换,根据其逆向还原代码如下

1
2
3
4
5
6
7
8
9
10
11
v12 = list('MbT3sQgX039i3g==AQOoMQFPskB1Bsc7')
v9 = 32
for i in range(0,v9,2):
v13 = v12[i]
v12[i] = v12[i + 1]
v12[i + 1] = v13

s = ""
s = v12[16:31]+v12[:15]

print("".join(s))

得到Base64编码

1
QAoOQMPFks1BsB7bM3TQsXg30i9g3==

这里对其解码需要用到源代码中的a类中的a方法,代码如下

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
package com.a.easyjni;

public class a {
private static final char[] a = {'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[] bArr) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i <= bArr.length - 1; i += 3) {
byte[] bArr2 = new byte[4];
byte b = 0;
for (int i2 = 0; i2 <= 2; i2++) {
if (i + i2 <= bArr.length - 1) {
bArr2[i2] = (byte) (b | ((bArr[i + i2] & 255) >>> ((i2 * 2) + 2)));
b = (byte) ((((bArr[i + i2] & 255) << (((2 - i2) * 2) + 2)) & 255) >>> 2);
} else {
bArr2[i2] = b;
b = 64;
}
}
bArr2[3] = b;
for (int i3 = 0; i3 <= 3; i3++) {
if (bArr2[i3] <= 63) {
sb.append(a[bArr2[i3]]);
} else {
sb.append('=');
}
}
}
return sb.toString();
}
}

就是一个自定义字典的Base64编码,所以需要利用这个字典对其进行解码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
base64_charset = ['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_charset = "".join(base64_charset)
print(base64_charset)
def Base64NewDecode(string):
base64_bytes = ["{:0>6}".format(str(bin(base64_charset.index(c))).replace('0b', ''))for c in string if c != '=']
base64_bytes_str = ''.join(base64_bytes)
print(base64_bytes_str)
for i in range(len(base64_bytes_str) // 8):
byte = base64_bytes_str[i * 8:i * 8 + 8]
print(chr(int(byte, 2)), end='')


Base64NewDecode(s)

得到flag:

1
flag{just_ANot#er_@p3}

输入成功

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2024 John Doe
  • 访问人数: | 浏览次数:

让我给大家分享喜悦吧!

微信