x2658y's Blog

杂七杂八的记事本

又是个apk, 直接上jadx, 反编译MainActivity, 直接找到关键算法

image-20220123173641201

Ctrl+C, Ctrl+V复刻

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <cstdio>

using namespace std;

int main()
{
char x[] = "dd2940c04462b4dd7c450528835cca15";
x[2] = (char)((x[2] + x[3]) - 50);
x[4] = (char)((x[2] + x[5]) - 48);
x[30] = (char)((x[31] + x[9]) - 48);
x[14] = (char)((x[27] + x[28]) - 97);
for (int i = 0; i < 16; i++) {
char a = x[31 - i];
x[31 - i] = x[i];
x[i] = a;
}
printf("flag{%s}", x);
}
//flag{59acc538825054c7de4b26440c0999dd}

是个apk, 拿AndroidKiller反编译, 找到一个Unicode字符串

image-20220123143139971

解码一看, 哈哈高兴的太早了

1
2
3
4
5
string = "\u7b54\u6848\u9519\u4e86\u80bf\u4e48\u529e\u3002\u3002\u3002\
\u4e0d\u7ed9\u4f60\u53c8\u4e0d\u597d\u610f\u601d\u3002\u3002\
\u3002\u54ce\u5440\u597d\u7ea0\u7ed3\u554a~~~"
print(string)
#答案错了肿么办。。。 不给你又不好意思。。 。哎呀好纠结啊~~~

MainActivity.smali里面找到了几个数组, 很可能和flag有关

image-20220123161621158

smali格式阅读起来很不方便, 需要将它还原成Java代码, 但是AndroidKiller不支持这种功能, 所以改用jadx.

jadx: Github

jadx-gui直接打开apk, 源码就出来了, 到MainActivity一看, 果然和flag有关

image-20220123163357380
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.example.findit;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity {
/* access modifiers changed from: protected */
@Override // android.support.v7.app.ActionBarActivity, android.support.v4.app.FragmentActivity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText edit = (EditText) findViewById(R.id.widget2);
final TextView text = (TextView) findViewById(R.id.widget1);
final char[] a = {'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'};
final char[] b = {'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'};
((Button) findViewById(R.id.widget3)).setOnClickListener(new View.OnClickListener() {
/* class com.example.findit.MainActivity.AnonymousClass1 */

public void onClick(View v) {
char[] x = new char[17];
char[] y = new char[38];
for (int i = 0; i < 17; i++) {
if ((a[i] < 'I' && a[i] >= 'A') || (a[i] < 'i' && a[i] >= 'a')) {
x[i] = (char) (a[i] + 18);
} else if ((a[i] < 'A' || a[i] > 'Z') && (a[i] < 'a' || a[i] > 'z')) {
x[i] = a[i];
} else {
x[i] = (char) (a[i] - '\b');
}
}
if (String.valueOf(x).equals(edit.getText().toString())) {
for (int i2 = 0; i2 < 38; i2++) {
if ((b[i2] < 'A' || b[i2] > 'Z') && (b[i2] < 'a' || b[i2] > 'z')) {
y[i2] = b[i2];
} else {
y[i2] = (char) (b[i2] + 16);
if ((y[i2] > 'Z' && y[i2] < 'a') || y[i2] >= 'z') {
y[i2] = (char) (y[i2] - 26);
}
}
}
text.setText(String.valueOf(y));
return;
}
text.setText("答案错了肿么办。。。不给你又不好意思。。。哎呀好纠结啊~~~");
}
});
}

public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}

好在Java的语法和C/C++类似, 可以大概看出来是个啥意思:

先对a数组进行处理, 放到x数组中, 对比用户的输入和x数组是否相同, 若相同则对b数组进行处理放到y数组中并显示出来, 想必y数组中的就是flag.

解密代码如下:

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
#include <cstdio>

using namespace std;

int main()
{
unsigned char a[] = { 'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e' };
unsigned char b[] = { 'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}' };
unsigned char x[64] = { 0 };
unsigned char y[64] = { 0 };
for (int i = 0; i < 17; i++) {
if ((a[i] < 'I' && a[i] >= 'A') || (a[i] < 'i' && a[i] >= 'a')) {
x[i] = (a[i] + 18);
}
else if ((a[i] < 'A' || a[i] > 'Z') && (a[i] < 'a' || a[i] > 'z')) {
x[i] = a[i];
}
else {
x[i] = (a[i] - '\b');
}
}
for (int i2 = 0; i2 < 38; i2++) {
if ((b[i2] < 'A' || b[i2] > 'Z') && (b[i2] < 'a' || b[i2] > 'z')) {
y[i2] = b[i2];
}
else {
y[i2] = (b[i2] + 16);
if ((y[i2] > 'Z' && y[i2] < 'a') || y[i2] >= 'z') {
y[i2] = (y[i2] - 26);
}
}
}
printf("Key:%s\nFlag:%s", x, y);
}
//Key:LzakAkLzwXdsyZgew
//Flag:flag{c164675262033b4c49bdf7f9cda28a75}
//用unsigned char是因为实测用char的话, 这个flag的算法在C/C++里会出现溢出而出现乱码,
//查了一下是因为Java的char是2字节. 这个flag的算法其实就是位移+16位类凯撒加密

无壳, 主函数大部分都是WindowsAPI, 关键是看这个函数

image-20220123141627396

再跟进去可以找到另一个函数

image-20220123141745338

还是没有啥有价值的信息, 但是找到了熟悉的函数, 在之前出现过, 见 刮开有奖

image-20220123142023366

果然, 答案就在这里了, 所以flag就是flag{1999902069a45792d233ac}

image-20220123142111899

这题也可以从字符串直接入手, 更快

还可以点19999次直接出flag
image-20220123142250428
0%