真的是签到

拿到是一个zip文件,并且没有后缀..手动给他加上.zip的后缀。

然后使用:ZipCenOp.jar 这个工具进行解密

1
2
java -jar ZipCenOp.jar e xxx.zip 加密
java -jar ZipCenOp.jar r xxx.zip 解密

然后就打开发现有一个“签到.exe”

然后用Die【我习惯了】这个工具看到这个是有一个ASPack(2.12-2.42)的壳

然后用Unpacker for ASPack 这个工具一键脱壳【我这里保存为222.exe】

image-20230920113319750

用IDA打开后发现还有问题,一看还有一个UPX的壳【但是这里的upx没给版本】

image-20230920113440314

我按照老样子去脱upx壳的时候不幸运的是错误了…【】

image-20230920114016765

然后我换了一个脱壳机【在52破解里面找的】,就成功了…

然后用IDA打开

image-20230920165855642

1
2
3
4
5
6
7
8
9
10
flag = ""
a = [0x6c,0x2f,0x30,0x31,0x32,0x33,0xb6,0xbf,0xa0,0xcf,0x7c,0x71,0x6a,0x6c,0x70,0x64,0x75,0x63]
b = a[::-1]
for i in range(18):
b[i] = b[i] ^ i

byte_obj = bytes(b)
output_string = byte_obj.decode('gb2312')
print(output_string)

批量生产的伪劣产品

这个题,我用jadx 打开后,好多好多没啥思路【我对安卓的逆向还是不熟练】不能熟练的找到位置。

我也很蒙蔽,太多了结果搜索出来了…意料之外

image-20230920172621629

来一个派森

拿到是一个exe文件

然后用die 看到是

image-20230920201542582

然后看了这篇文章:https://cloud.tencent.com/developer/article/1872447

使用:pyinstxtractor.py 脚本提取pyc文件

1
python pyinstxtractor.py check.exe

得到一个exe_extracted的文件夹,然后再该文件夹中找到check.pyc文件然后再用在线的pyc反编译

我用的pyc 反编译的在线:https://www.toolkk.com/tools/pyc-decomplie

然后得到

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
# uncompyle6 version 3.8.0
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.7.0 (default, Nov 25 2022, 11:07:23)
# [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
# Embedded file name: checkme.py


def b58encode(tmp: str) -> str:
tmp = list(map(ord, tmp))
temp = tmp[0]
base58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
for i in range(len(tmp) - 1):
temp = temp * 256 + tmp[(i + 1)]

tmp = []
while 1:
tmp.insert(0, temp % 58)
temp = temp // 58
if temp == 0:
break

temp = ''
for i in tmp:
temp += base58[i]

tmp = []
for i in range(len(temp)):
tmp.append(chr(ord(temp[i]) ^ i))

check = [
'A', '5', 'q', 'O', 'g', 'q', 'd', '\x7f', '[', '\x7f', 's', '{', 'G', 'A', 'x', '`', 'D', '@', 'K', 'c', '-', 'c', ' ', 'G', '+', '+', '|', 'x', '}', 'J', 'h', '\\', 'l']
if tmp == check:
return 1
else:
return 0


flag = input('输入flag:')
if b58encode(flag):
print('you win')
else:
print('try again')
# okay decompiling /tmp/650ae1e957967.pyc

这里比较简单虽然我不算懂,但是看到了base58 和 ^

1
2
3
4
5
6
7
8
9
10
11
12
check = [ 'A', '5', 'q', 'O', 'g', 'q', 'd', '\x7f', '[', '\x7f', 's', '{', 'G', 'A', 'x', '`', 'D', '@', 'K',
'c', '-', 'c', ' ','G', '+', '+', '|', 'x', '}', 'J', 'h', '\\', 'l']
flag = ""
#xor 回去然后base58解码
m=0
for i in range(len(check)):
temp = ord(check[i])^i
flag += chr(temp)

print(flag)
#A4sLctbxSvypKLvoTQYp9v6P32fcaWvCL
#然后再去base58解密就得到flag

好好学习天天向上

emm这个题我吐了

image-20230921193508149

反正就是做不出来,最后看了Wp发现flag居然是猜到的

因为我们分析过后可以得知:flag是32个字符,然后是由于:abdefglostuyp{}_ 这几个字符组成的

flag{good_good_study_day_day_up}

屏幕裂开了

首先通过999 99定位位置【我凭着感觉看的】

image-20230921195022295

点进去发现有一个checkflag,它有2个传参,感觉这个就是咱们要找的地方

image-20230921220558935

然后看它在哪里被调用了,就在该目录的下面。

image-20230921220801328

这里传了一个s还有一个啥东西【因为我没学过java我也不懂,菜鸡一个】

但是我这里找不到这个checkflag的函数在哪里….

不得不说Ai是真的好用….【大佬绕过】

image-20230921221143484

知道这个native 是需要在本地库中完成的。本地库….但是我就一个apk文件,用jadx打开就是这样的,我咋才能找到这个呢?

后来我通过搜索了“native”这个关键字,但是大多都是介绍和实现这种方法的。也没告诉我这个玩意在哪里找..

百思不得其解的时候,我搜索了“CTF java native”这个关键字。成功的让我搜索到了一篇文章

https://www.cnblogs.com/CimeLi/p/12285410.html

通过里面的方法我得到了“apk解压”这几个字。但是作为一个小白,我也不会。所以我去百度又搜索了一下apk如何解压。

得到了如下的答案:APK文件的后缀为. apk,但是其格式是压缩文件zip的格式。 可以通过WinZip、WinRAR等将其解压。 直接将该文件扩展名改为.zip就可以解压该文件来了解APK文件的结构。

然后我就把apk改成了zip,成功的解压了。

image-20230921221735170

从上一篇文章中可得到so文件在lib下。但是这里又4个文件…

看到native下面有一个:System.loadLibrary(“native-lib”);

通过尝试最后确定为:\armeabi-v7a文件夹下的libnative-lib.so文件

我们用IDA将它打开,进去也是一脸蒙蔽,但是通过左边的函数窗口搜索“checkflag”

image-20230921222148788

然后就得到了:

image-20230921225659837

下面那个for循环还是比较容易看懂的,现在的问题就是不知道v9是什么,然后StringUTFChars是什么。

但是通过strlen(StringUTFChars) == 63这个,我猜测是flag的长度,flag也是我们输入的东西。回到jadx里面。在调用checkflag的时候传入了2个参数

1
mainActivity.checkflag(mainActivity.s, MainActivity.this.input.getText().toString())

后面:MainActivity.this.input.getText().toString() 这一个长串的感觉是我们输入的flag,前面的

mainActivity.s是char[] s = new char[256];但是上面的代码对s进行了处理。我们现在就来分析一下这个s是什么。

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
String key = "InfinityLoop";
char[] s = new char[256];
char[] k = new char[256];

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (char i = 0; i < 256; i = (char) (i + 1)) {
this.s[i] = i; // s中存放的是0到255
}
for (int i2 = 0; i2 < 256; i2++) {
char[] cArr = this.k;
String str = this.key;
cArr[i2] = str.charAt(i2 % str.length());//虽然没学过,但是这个i%len太明显了,这里很明显的就是 将InfinityLoop字符串,反复的放入cArr里面
}
this.btn = (Button) findViewById(R.id.button);
this.btn2 = (Button) findViewById(R.id.button2);
this.btn.setOnClickListener(new View.OnClickListener() { // from class: com.zxc.ClickStorm.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
MainActivity.this.hit_count++;
char j = 0;
for (char i3 = 0; i3 < 256; i3 = (char) (i3 + 1)) {
j = (char) (((MainActivity.this.s[i3] + j) + MainActivity.this.k[i3]) % 256);
//上面这一行是将s[]里面的数据打乱。
char tmp = MainActivity.this.s[i3];
MainActivity.this.s[i3] = MainActivity.this.s[j];
MainActivity.this.s[j] = tmp;//上面3行是一个交换
}
MainActivity.this.btn.setText(String.valueOf(MainActivity.this.hit_count));
if (MainActivity.this.hit_count >= 99999) {
MainActivity.this.btn2.setEnabled(true);
}
}
});

看到:MainActivity.this.hit_count >= 99999就知道首先要循环这么多次

j = (char) (((MainActivity.this.s[i3] + j) + MainActivity.this.k[i3]) % 256);
//上面这一行是将s[]里面的数据打乱。
char tmp = MainActivity.this.s[i3];
MainActivity.this.s[i3] = MainActivity.this.s[j];
MainActivity.this.s[j] = tmp;//上面3行是一个交换

这里就是将s中的数据打乱的代码

这里来详细分析一下s和k吧:

这段代码中,sk 是两个字符数组,分别用于存储操作数据和密钥。它们被用于一个点击计数器应用的加密操作。以下是它们的具体操作:

  1. s 数组初始化:
    onCreate 方法中,s 数组被初始化为包含 256 个字符的数组,每个字符都是一个从 0 到 255 的连续整数。这表示 s 数组最初包含了所有可能的字节值。

  2. k 数组初始化:
    同样在 onCreate 方法中,k 数组被初始化为一个与字符串 key 相关的字符数组。这个字符串似乎是 “InfinityLoop”。初始化过程将密钥字符串的字符循环放置到 k 数组中,如果 key 字符串的长度小于 256,则会重复使用字符串的字符。

  3. onClick 方法中的操作:
    在按钮点击事件处理程序中,onClick 方法执行以下操作:

    • hit_count 被递增,记录点击次数。
    • 通过一个循环迭代处理 s 数组中的每个元素,以及 k 数组中的相应元素。
    • 在每次循环迭代中,j 的值被计算为 ((s[i3] + j) + k[i3]) % 256,其中 % 表示取模操作。
    • 然后,s 数组中的第 i3 个元素与 s 数组中的第 j 个元素交换位置。

    这个操作看起来是一种类似于加密的操作,根据点击次数以及预先初始化的 s 数组和 k 数组进行数据交换。每次点击后,s 数组的状态会发生改变,从而导致数据的混淆和加密效果。当点击次数达到 99999 时,按钮 btn2 会被启用,这似乎是一个触发条件。

总之,sk 数组在 onClick 方法中被用于执行一种加密操作,其中 s 用于存储数据,k 用于存储密钥,每次点击都会导致 s 数组的变换。

用python实现一下:

1
2
3
4
5
6
7
8
9
10
s = [i for i in range(256)]
k = (b"InfinityLoop" * 22)[0:256]
for a in range(99999):
j = 0
for i in range(256):
j = (s[i] + j + k[i]) % 256
temp = s[j]
s[j] = s[i]
s[i] = temp
print(s)

然后就得到了这个s

后面就是将s和flag传进去.so文件,然后和里面的answer进行比较,这个后面的还是比较好理解的了。中间有一个不太好理解的v9[i] = *(_BYTE *)(CharArrayElements + 2 * i);

但是上面的文章说没用…那就没用吧

其实我不放心还问了Ai:

image-20230921225613261

OKOK,后面就不需要分析了吧

最后的代码:

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
s = [i for i in range(256)]
k = (b"InfinityLoop" * 22)[0:256]
for hit_count in range(99999):
j = 0
for i in range(256):
j = (s[i] + j + k[i]) % 256
temp = s[j]
s[j] = s[i]
s[i] = temp
print(s)

answer = [0xA6, 0x3D, 0x54, 0x0B0, 0x74, 0xCC, 0xBD, 0x2A, 0x4A, 0x0DE, 0x0BD, 0x35, 0x0D1, 0x1D, 0x80, 0x32, 0x5F,
0x64, 0x2F, 0x0C5, 0x0DD, 0x11, 0x3E, 0x95, 0x0CC, 0x17, 0x13, 0x0E5, 0x5E, 0x65, 0x0CE, 0x42, 0x9E, 0x47,
0x0C8, 0x0F3, 0x4D, 0x8A, 0x0A6, 0x1F, 0x0F0, 0x50, 0x27, 0x0A2, 0x28, 0x81, 0x24, 0x0A7, 0x0B4, 0x90, 0x0FC,
0x93, 0x8A, 0x0C1, 0x77, 0x0D5, 0x16, 0x1E, 0x0FD, 0x87, 0x0C7, 0x0BB, 0x0B3,]

v7 = 0
v6 = 0
v9 = s
flag = ''
for i in range(63):
v7 = v7 +1
v6 = (v6 + v9[v7]) & 0xff
v5 = v9[v7]
v9[v7] = v9[v6]
v9[v6] = v5
flag += chr(answer[i] ^ v9[(v9[v7] +v9[v6])%256])

print(flag)

v6 = (v6 + v9[v7]) & 0xff

如果不进行 & 操作就会报错:超出范围

因为IDA分析出来是:v6 = (unsigned __int8)(v6 + v9[v7]);

&0xff的作用是截断

  • 而跟& 0xff运算的意义其实就是截断,将123456的高位右移8位,通过0xff截取出来。 实际意义就是取字节,比如一个4字节的数,需要将每个字节内容取出来转换出目标数据,那么通过>> 并且&0xff 运算就可以去除想要的部分。

v9[(v9[v7] +v9[v6])%256]

如果不进行%256也会报错:超出范围

同理:(unsigned __int8)(v9[v7] + v9[v6])

所以需要:%256