Reverse

去花
img
按c强制生成代码
img
第二处
img
第三处
img
在0x00401500处按P生成函数
主函数,RC4加密,key是WOWOWOWWOWOWOW
img

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Cipher import ARC4
a =[
0xF4, 0x87, 0xD4, 0xFA, 0x61, 0xA6, 0x71, 0x12, 0x75, 0x09,
0xFE, 0xD8, 0xE4, 0x38, 0x97, 0x51, 0xA8, 0xDF, 0x85, 0x65,
0xC2, 0xB2, 0x15, 0xEF, 0x1F, 0xEC, 0x69, 0xDD, 0x6E, 0xE9,
0xCF, 0x07, 0xAE, 0xC8, 0x17, 0xF0, 0x65, 0x72, 0xE6, 0x73,
0xA4, 0x0C, 0x87, 0x64, 0x9E, 0x9E, 0x71, 0x8C, 0x7F, 0xD7,
0x75, 0x84]
key = b'WOWOWOWWOWOWOW'
enc = b''.join([bytes([i]) for i in a])
rc4 = ARC4.new(key)
decrypted_data = rc4.decrypt(enc)
print(decrypted_data.decode('utf-8'))
# flag{You!FlowerMaster!YouPassTheThirdPZGALAXYlevel!}

flag

flag{You!FlowerMaster!YouPassTheThirdPZGALAXYlevel!}

STL

C++逆向,大致能读懂相关函数
img
直接爆破,脚本跑得有点慢,大致20min能出结果

exp

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
from Crypto.Util.number import long_to_bytes
enc = [
0x2882D802120E, 0x28529A05954, 0x486088C03, 0xC0FB3B55754,
0xC2B9B7F8651, 0xAE83FB054C, 0x29ABF6DDCB15, 0x10E261FC807,
0x2A82FE86D707, 0xE0CB79A5706, 0x330560890D06]
hex_list = [0]
flag = ''
for v in enc:
tmp = ''
for v7 in range(0x90000,0x70000000): # 这个爆破范围是用enc的最大值最小值右移15计算出来的
if ((v7 << 15) ^ v7) == v:
#print(long_to_bytes(v7),hex(v),hex(v7)[2:].zfill(8))
tmp = hex(v7)[2:].zfill(8) # enc有些值爆破出来的值不足8位,用zill补齐
#print(tmp)
for i in range(len(tmp)-1,0,-2): # 小端序的处理
hex_list.append(int(tmp[i-1:i+1],16))
#hex_list = [int(tmp[i:i+2], 16) for i in range(0, len(tmp), 2)]
break


print(hex_list)
for i in range(len(hex_list)-2,0,-1): # 异或处理
hex_list[i] ^= hex_list[i+1]
for i in hex_list[::-1]: # 翻转在这里
flag += chr(i)
print(flag)
# [0, 14, 18, 5, 81, 84, 89, 10, 5, 3, 12, 9, 0, 84, 87, 31, 24, 81, 6, 87, 24, 76, 5, 93, 1, 21, 75, 87, 83, 7, 72, 28, 2, 7, 87, 5, 85, 6, 87, 25, 28, 6, 13, 10, 102]
# flag{b53fc431-eb1f-89da-5bd5-2e1184728a5das}

flag

flag{b53fc431-eb1f-89da-5bd5-2e1184728a5das}

EzDLL

主程序中分析
img
key是
img
看看dll中的encrypt,是魔改过的xtea,注意魔改的地方
img
然后写脚本解,结果发现已知解不出来
倒回去重新分析主程序发现在TLS中加入了反调试,并且修改了xtea的key
img
计算一下真正的key是[5,20,13,14]
刚刚臭不可闻的key瞬间就浪漫起来了

exp

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
void decrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], delta = 999999999, sum = delta * num_rounds + 1;
for (i=0; i < num_rounds; i++) {
v1 -= (((v0 << 3) ^ (v0 >> 4)) + v0) ^ (sum + key[(sum>>11) & 3]);
sum -= delta;
v0 -= (((v1 << 3) ^ (v1 >> 4)) + v1) ^ (sum + key[sum & 3]);

}
v[0] = v0; v[1] = v1;
}
void encrypt(unsigned int *a1, uint32_t const a2[4]) {
unsigned int v3; // [rsp+24h] [rbp+4h]
unsigned int v4; // [rsp+44h] [rbp+24h]
unsigned int v5; // [rsp+64h] [rbp+44h]
unsigned __int64 i; // [rsp+A8h] [rbp+88h]

v3 = *a1;
v4 = a1[1];
v5 = 1;
for ( i = 0; i < 0x21; ++i )
{
v3 += (a2[(v5 & 3)] + v5) ^ (v4 + ((v4 >> 4) ^ (8 * v4)));
v5 += 999999999;
v4 += (a2[(v5 >> 11) & 3] + v5) ^ (v3 + ((v3 >> 4) ^ (8 * v3)));
}
*a1 = v3;
a1[1] = v4;
}
int main() {
uint32_t const k[4] = {5,20,13,14};
uint32_t enc[] = {
0x89A34382, 0xC880BA6F, 0xBD56B4F8, 0x8DB241B3, 0x040E44DA,
0xDE382E03, 0x89AD5412, 0x21633095, 0x11940DDF, 0x11D0B2DC};
unsigned int r = 33;
uint32_t test[] = {305402420,287454020};
encrypt(test,k);
//printf("%u %u\n",test[0], test[1]);
//decrypt(r,test,k);
//printf("%u %u\n",test[0], test[1]);
for(int i=8; i>=0; i-=2){
decrypt(r, &enc[i], k);
}
printf("Decrypted data is: %s\n",(char*)enc);
return 0;
}
//Decrypted data is: flag{Ca1l_y0u_3ven_1f_w3_@r3_f@r_Apart!}@

flag

flag{Ca1l_y0u_3ven_1f_w3_@r3_f@r_Apart!}

pyexe

直接pyinstxtractor,得到的文件是加密过的。注意使用和源文件同版本python,不然会出现如下skipping pyz extraction的问题
img
img

在反编译python生成可执行文件exe时,引用的类库文件经常遇到使用Crypto模块AES算法加密,解包生成的并不是pyc文件,而是加密的pyc.encrypted文件,它无法查看编译。
第一步,获取Crypto的key,这是打包时由开发者指定的。解包完成后将在根目录形成名为 pyimod00_crypto_key.pyc 的文件,将它转为py文件即可查看key文件。
第二步,编写解密处理的脚本代码

我们先获取key = '00000000new1star'
img
然后用脚本fix

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
import glob
import zlib
import tinyaes
from pathlib import Path

CRYPT_BLOCK_SIZE = 16

# key obtained from pyimod00_crypto_key
key = bytes('00000000new1star', 'utf-8')

for p in Path("./fakekey.exe_extracted/PYZ-00.pyz_extracted").glob("**/*.pyc.encrypted"):
inf = open(p, 'rb') # encrypted file input
outf = open(p.with_name(p.stem), 'wb') # output file

# Initialization vector
iv = inf.read(CRYPT_BLOCK_SIZE)

cipher = tinyaes.AES(key, iv)

# Decrypt and decompress
plaintext = zlib.decompress(cipher.CTR_xcrypt_buffer(inf.read()))

# Write pyc header
# The header below is for Python 3.8
outf.write(b'\x55\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0')

# Write decrypted data
outf.write(plaintext)

inf.close()
outf.close()

# Delete .pyc.encrypted file
p.unlink()
"""
Python 2.7: \x03\xf3\x0d\x0a\0\0\0\0
Python 3.0: \x3b\x0c\x0d\x0a\0\0\0\0
Python 3.1: \x4f\x0c\x0d\x0a\0\0\0\0
Python 3.2: \x6c\x0c\x0d\x0a\0\0\0\0
Python 3.3: \x9e\x0c\x0d\x0a\0\0\0\0\0\0\0\0
Python 3.4: \xee\x0c\x0d\x0a\0\0\0\0\0\0\0\0
Python 3.5: \x17\x0d\x0d\x0a\0\0\0\0\0\0\0\0
Python 3.6: \x33\x0d\x0d\x0a\0\0\0\0\0\0\0\0
Python 3.7: \x42\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0
Python 3.8: \x55\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0
Python 3.9: \x61\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0
Python 3.10: \x6f\x0d\x0d\x0a\0\0\0\0\0\0\0\0\0\0\0\0
"""

然后uncompyle6 反编译 fakekey.pyc得到源码

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# uncompyle6 version 3.9.0
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.9.11 (tags/v3.9.11:2de452f, Mar 16 2022, 14:33:45) [MSC v.1929 64 bit (AMD64)]
# Embedded file name: fakekey.py
import base64
from settings import key

def eval_(data):
return str(base64.b64decode(data))[2:-1]


consefued2 = [b'MA==', b'MQ==', b'Mg==', b'Mw==', b'NA==', b'NQ==', b'Ng==', b'Nw==', b'OA==',
b'OQ==', b'YQ==', b'Yg==', b'Yw==', b'ZA==', b'ZQ==', b'Zg==', b'Zw==',
b'aA==', b'aQ==', b'ag==', b'aw==', b'bA==', b'bQ==', b'bg==', b'bw==',
b'cA==', b'cQ==', b'cg==', b'cw==', b'dA==', b'dQ==', b'dg==', b'dw==',
b'eA==', b'eQ==', b'eg==', b'QQ==', b'Qg==', b'Qw==', b'RA==', b'RQ==',
b'Rg==', b'Rw==', b'SA==', b'SQ==', b'Sg==', b'Sw==', b'TA==', b'TQ==',
b'Tg==', b'Tw==', b'UA==', b'UQ==', b'Ug==', b'Uw==', b'VA==', b'VQ==',
b'Vg==', b'Vw==', b'WA==', b'WQ==', b'Wg==', b'IQ==', b'Ig==', b'Iw==',
b'JA==', b'JQ==', b'Jg==', b'Jw==', b'KA==', b'KQ==', b'Kg==', b'Kw==',
b'LA==', b'LQ==', b'Lg==', b'Lw==', b'Og==', b'Ow==', b'PA==', b'PQ==',
b'Pg==', b'Pw==', b'QA==', b'Ww==', b'XA==', b'XQ==', b'Xg==', b'Xw==',
b'YA==', b'ew==', b'fA==', b'fQ==', b'fg==', b'IA==', b'CQ==', b'Cg==',
b'DQ==', b'Cw==', b'DA==', b'YmFzZTY0LmI2NGRlY29kZQ==']

def change_fun2(fun):

def real_fun(*args):
ans = fun(*args)
ans.pop(eval(eval(eval_(b'YmFzZTY0LmI2NGRlY29kZQ==') + '(consefued2[0])')) - 3)
ans.pop(eval(eval(eval_(b'YmFzZTY0LmI2NGRlY29kZQ==') + '(consefued2[5])')))
return ans

return real_fun


def main(plaintext):
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
else:
i, j = (0, 0)
keystream = []
for _ in range(len(plaintext)):
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) % 256]
keystream.append(k)
else:
ciphertext = []
for i in range(len(plaintext)):
ciphertext.append(ord(plaintext[i]) ^ keystream[i] ^ 5)
else:
return ciphertext


@change_fun2
def encode1(text):
res = main(text)
return res


def encode2(xxxx):
return base64.b64encode((b'').join([chr(t).encode() for t in xxxx]))


if __name__ == '__main__':
print('你好,祝您游戏愉快')
flag = input('请输入flag (flag的长度为20)> ')
b = encode2(encode1(flag))
assert len(flag) == 20
while True:
if b == b'IMKJw4jCkgQxw6A1w5QRw7A5SG14wobDs8KF':
print('you are right!')
break
else:
print('work hard')

首先从 settings import了一个key
看看\PYZ-00.pyz_extracted里面的settings.pyc
img
源代码不难理解,主要是changefun2,让GPT解释一下
img
img
main函数是一个rc4然后异或5
所以整体加密就是rc4加密后异或5,接着pop加密后的flag的第5和-3位,最后base64
我们只需爆破第5位和第-3位字符即可

exp

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
import base64
import hashlib
def rc4(plaintext):
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
# 生成密钥流
i = j = 0
keystream = []
for _ in range(len(plaintext)):
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) % 256]
keystream.append(k)
ciphertext = []
for i in range(len(plaintext)):
ciphertext.append(ord(plaintext[i]) ^ keystream[i] ^ 5)
return ciphertext
def decode2(encoded_str):
decoded_str = base64.b64decode(encoded_str).decode()
return [ord(t) for t in decoded_str]
def decode(data):
for a in range(256):
for b in range(256):
tmp_list = data[:5]+ [a] +data[5:-2]+ [ b,data[-2],data[-1]] #拼接还原
string_tmp = "".join([chr(t) for t in tmp_list])
flag = "".join([chr(t) for t in rc4(string_tmp)])
if hashlib.md5(flag.encode()).hexdigest()[:6] == "cbd746":
print(flag)
a = b'IMKJw4jCkgQxw6A1w5QRw7A5SG14wobDs8KF'
key = b'new_star'
decode(decode2(a))
# flag{no_ke1_no_fuN!}

flag

flag{no_ke1_no_fuN!}

Let’s Go

go逆向,直接用ida7.7
先看main_init,iv异或了0x32
img
接着看main_main
img
img
动调看看iv是什么
img
iv是和key一样,但是记得异或0x32
img

exp

1
2
3
4
5
6
7
8
9
from Crypto.Cipher import AES
iv = [0x4E, 0x65, 0x77, 0x53, 0x74, 0x61, 0x72, 0x21, 0x4E, 0x65, 0x77, 0x53, 0x74, 0x61, 0x72, 0x21]
iv = bytes([i ^ 0x32 for i in iv])
# print(iv)
key = b'NewStar!NewStar!'
enc = bytes.fromhex("ee01674b13ff8dd86f8e481aa86f5d25e773a3fd0338f60988cb738b8b178c44")
aes = AES.new(key, mode=AES.MODE_CBC, iv=iv)
print(aes.decrypt(enc))
# b"flag{It's_time_to_Go!!!Let's_Go}"

flag

flag{It's_time_to_Go!!!Let's_Go}

ez_chal_1

主函数分析如下
img
再看看sub_401170,明显xtea,多了个异或
img
动调获取key
img

exp

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string>
#include <string.h>
void decrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], delta = 0x9E3778B9, sum = delta * num_rounds;
for (i=0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]) ^ v0;
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]) ^ v1;

}
v[0] = v0; v[1] = v1;
}
char a[40] = {0};
int main() {
uint32_t k[4] = {0};
uint32_t enc[] = {
0xC19EA29C,0xDC091F87,0x91F6E33B,0xF69A5C7A,
0x93529F20,0x8A5B94E1,0xF91D069B,0x23B0E340};
unsigned int r = 64;
memcpy(a,"NewStar!NewStar!",strlen("NewStar!NewStar!"));
for (int i = 0; i < strlen(a); i+=4) {
k[i / 4] = *(uint32_t*)(a + i);
//printf("%u ",k[i/4]);
}
for(int i=0; i<8; i+=2){
decrypt(r, &enc[i], k);
}
printf("Decrypted data is: %s\n",(char*)enc);
return 0;
}
//Decrypted data is: flag{Let's_Dr1nk_A_Cup_Of_Te4!!}NewStar!NewStar!

flag

flag{Let's_Dr1nk_A_Cup_Of_Te4!!}

Andronative

调用了native层的encode,传入输入和deadbeef
img
解包之后查看librunfaraway.so的encode函数
先看看最后的check,最后的结果经过test后与
6DTNDRc1uJyyyd/GrT+bW3NNIoXzPQyK722kicTTFTTySTzyRE+BWdXnW6zF7UY9iUZK27QN 比较
img
查看test,应该是base64
img
往前看,对v22赋完值(0~0xff)后,进行了_ROL2_移位
img
_ROL2_是ida内置函数,在ida/plugins/defs.h中可以查看到定义
img
再看看_ROL_函数
img

有两个参数:(value, int count)
第一个参数为左移的数,第二个参数为左移的位数。
如果第二个参数值为负数,则实际上为循环右移 -count位。
该函数的实现细节为:
先得到value的位数,然后count对位数取模。
如果count值大于0,则先右移-count取模的结果,然后在左移取模的结果,得到的两个数相或,即为循环左移的结果。
如果count值小于0,先左移在右移即可。
举例来说: value = 0110, count = 6
value为4位数, 6 % 4 = 2,
0110先右移4-2=2位,得到0001,然后在左移2位,得到1000,0001 | 1000结果为1001,即循环左移结果为1001。

知道ROL2的含义后只需将v22的数据用lazyida dump出来(其实就是0-255)
然后自己写个c模拟一下,可以得到所需的box表

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
#include <stdio.h>
#include <stdint.h>

uint16_t __ROL2__(uint16_t value, int count) {
return (value << count) | (value >> (16 - count));
}

int main() {
uint16_t v22[128]; // 128个uint16_t等于16个__int128

// 初始化v22,每两个字节存储为一个uint16_t
uint16_t initData[16][8] = {
{0x0100, 0x0302, 0x0504, 0x0706, 0x0908, 0x0B0A, 0x0D0C, 0x0F0E},
{0x1110, 0x1312, 0x1514, 0x1716, 0x1918, 0x1B1A, 0x1D1C, 0x1F1E},
{0x2120, 0x2322, 0x2524, 0x2726, 0x2928, 0x2B2A, 0x2D2C, 0x2F2E},
{0x3130, 0x3332, 0x3534, 0x3736, 0x3938, 0x3B3A, 0x3D3C, 0x3F3E},
{0x4140, 0x4342, 0x4544, 0x4746, 0x4948, 0x4B4A, 0x4D4C, 0x4F4E},
{0x5150, 0x5352, 0x5554, 0x5756, 0x5958, 0x5B5A, 0x5D5C, 0x5F5E},
{0x6160, 0x6362, 0x6564, 0x6766, 0x6968, 0x6B6A, 0x6D6C, 0x6F6E},
{0x7170, 0x7372, 0x7574, 0x7776, 0x7978, 0x7B7A, 0x7D7C, 0x7F7E},
{0x8180, 0x8382, 0x8584, 0x8786, 0x8988, 0x8B8A, 0x8D8C, 0x8F8E},
{0x9190, 0x9392, 0x9594, 0x9796, 0x9998, 0x9B9A, 0x9D9C, 0x9F9E},
{0xA1A0, 0xA3A2, 0xA5A4, 0xA7A6, 0xA9A8, 0xABAA, 0xADAC, 0xAFAE},
{0xB1B0, 0xB3B2, 0xB5B4, 0xB7B6, 0xB9B8, 0xBBBA, 0xBDBC, 0xBFBE},
{0xC1C0, 0xC3C2, 0xC5C4, 0xC7C6, 0xC9C8, 0xCBCA, 0xCDCC, 0xCFCE},
{0xD1D0, 0xD3D2, 0xD5D4, 0xD7D6, 0xD9D8, 0xDBDA, 0xDDDC, 0xDFDE},
{0xE1E0, 0xE3E2, 0xE5E4, 0xE7E6, 0xE9E8, 0xEBEA, 0xEDEC, 0xEFEE},
{0xF1F0, 0xF3F2, 0xF5F4, 0xF7F6, 0xF9F8, 0xFBFA, 0xFDFC, 0xFFFE},
};

for(int i = 0; i < 16; i++) {
for(int j = 0; j < 8; j++) {
v22[i * 8 + j] = initData[i][j];
}
}

for(int i = 1; i < 127; i += 2) {
v22[i] = __ROL2__(v22[i], 8);
v22[i+1] = __ROL2__(v22[i+1], 8);
}
for(int i = 0; i < 128; i++) {
printf("%d, %d", v22[i] & 0xFF, (v22[i] >> 8) & 0xFF);
if (i != 127) {
printf(", ");
}
if (i % 8 == 7) {
printf("\n");
}
}

return 0;
}

img
之后的加密逻辑其实就是和传进来的key异或,然后执行ROL1移位
ROL1根据以上分析其实就等效于

1
2
3
uint16_t __ROL1__(uint16_t value, int count) {
return (value << count) | (value >> (8 - count));
}

据此可以写出exp

exp

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
import base64
def custom_base64_decode(data):
custom_chars = 'TcdXDE6oNOPQRUepFGHwxfIJKnklSqrsyz+/LYZVW123ij789ABCMtuvgab045hm'
# 创建标准Base64字符集和自定义字符集的映射表
base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
char_map = str.maketrans( custom_chars, base64_chars)
# 替换为自定义字符
data = data.translate(char_map)
# 使用标准Base64解码
decoded_data = base64.b64decode(data.encode())
return decoded_data
enc = '6DTNDRc1uJyyyd/GrT+bW3NNIoXzPQyK722kicTTFTTySTzyRE+BWdXnW6zF7UY9iUZK27QN'

result = list(custom_base64_decode(enc))
print(result)

box = [0, 1, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14,
17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30,
33, 32, 35, 34, 37, 36, 39, 38, 41, 40, 43, 42, 45, 44, 47, 46,
49, 48, 51, 50, 53, 52, 55, 54, 57, 56, 59, 58, 61, 60, 63, 62,
65, 64, 67, 66, 69, 68, 71, 70, 73, 72, 75, 74, 77, 76, 79, 78,
81, 80, 83, 82, 85, 84, 87, 86, 89, 88, 91, 90, 93, 92, 95, 94,
97, 96, 99, 98, 101, 100, 103, 102, 105, 104, 107, 106, 109, 108, 111, 110,
113, 112, 115, 114, 117, 116, 119, 118, 121, 120, 123, 122, 125, 124, 127, 126,
129, 128, 131, 130, 133, 132, 135, 134, 137, 136, 139, 138, 141, 140, 143, 142,
145, 144, 147, 146, 149, 148, 151, 150, 153, 152, 155, 154, 157, 156, 159, 158,
161, 160, 163, 162, 165, 164, 167, 166, 169, 168, 171, 170, 173, 172, 175, 174,
177, 176, 179, 178, 181, 180, 183, 182, 185, 184, 187, 186, 189, 188, 191, 190,
193, 192, 195, 194, 197, 196, 199, 198, 201, 200, 203, 202, 205, 204, 207, 206,
209, 208, 211, 210, 213, 212, 215, 214, 217, 216, 219, 218, 221, 220, 223, 222,
225, 224, 227, 226, 229, 228, 231, 230, 233, 232, 235, 234, 237, 236, 239, 238,
241, 240, 243, 242, 245, 244, 247, 246, 249, 248, 251, 250, 253, 252, 254, 255]
key = 'deadbeef'
flag = ''
for i,val in enumerate(result):
val = ((val << 3) | (val >> 5)) & 0xff
val = ((val << 2) | (val >> 6)) & 0xff
val ^= ord(key[i % len(key)])
flag += chr(box.index(val))
print(flag)
# [24, 64, 8, 16, 192, 105, 217, 120, 32, 128, 40, 209, 120, 8, 186, 162, 178, 8, 88, 112, 225, 40, 184, 24, 186, 170, 154, 176, 16, 0, 64, 0, 32, 112, 8, 96, 48, 88, 178, 160, 32, 217, 160, 104, 80, 184, 217, 112, 176, 217, 152, 170, 226, 200]
# flag{I_hate_le333ekk_asd213sadlgajaieo2sa_this_is_s0?}

flag

flag{I_hate_le333ekk_asd213sadlgajaieo2sa_this_is_s0?}
除此之外还可以考虑动态调试来获取box
先配置一下ida调试所需要的android_server,先打开模拟器
然后在命令行中依次输入以下命令

1
2
3
4
5
6
7
8
9
10
11
adb devices
adb push xxx\dbgsrv\android_server /data/local/tmp #ida目录下的dbgsrv中的android_server,路径自行修改
adb push xxx\dbgsrv\android_server64 /data/local/tmp
adb shell
su
cd /data/local/tmp
ls -l
chmod 777 android_server
chmod 777 android_server64
ls -l
./android_server64

img
再新开一个命令行,执行adb forward tcp:23946 tcp:23946转发端口
然后打开DDMS(没有的的请自行安装android sdk
img
以debug模式启动进程

1
adb shell am start -D -n com.chick.runfaraway/com.chick.runfaraway.MainActivity #包名/包名.入口Activity

提示Waiting for debugger
ida 打开选择Debugger->Attach->Remote ARM Linux/Android debugger
img
sebug option设置和端口设置
img
找到要attach的进程进行attach
img
返回DDMS,查看进程端口
img
恢复java层运行,注意8626是DDMS里查询到的端口号。

1
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8626

此时就可以开始动态调试了,具体调试过程就不写了

2hard2u

用 JEB 打开(JEB自带反混淆,用JADX打开无法去除BlackObfuscator混淆)
先看看 MainActivity 的 oncreat函数
2023-11-16T183928
可以看到 用密钥friedchick对输入进行 encrypt
然后对结果进行encode,encode是调用了native层
2023-11-16T184420
然后观察一下 encrypt 函数,有KSA,RPGA,可以确定是RC4
2023-11-16T184536
接下来分析 native 层
解包分析 libit2hard2u.so
可以看到是一个 Tea
2023-11-16T184933
那么我们先解Tea

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
#include <stdio.h>
#include <stdint.h>

void encrypt (uint32_t* v, uint32_t* k) {
uint32_t sum = 0;
uint32_t v0 = v[0], v1 = v[1];
uint32_t delta = 0x9e3779b9;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];

for (int i=0; i<32; i++) {
sum += delta;
v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
}

v[0]=v0;
v[1]=v1;
}

void decrypt (uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1];
uint32_t delta = 0x9e3779b9;
uint32_t sum = delta * 32;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];

for (int i=0; i<32; i++) {
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum -= delta;
}

v[0]=v0;
v[1]=v1;
}

int main(){
uint32_t v[12] = {1709447429,319073545,933829062,996866537,2967913332,1503747157,2184587230,4251507117,351303914,3390489858,1838058664,2531479130};
uint32_t k[4]= {5,2,7,5};
uint32_t tmp[2];
for(int i=0; i<12; i+=2){
tmp[0] = v[i]; tmp[1] = v[i+1];
decrypt(tmp,k);
printf("0x%x 0x%x ", tmp[0], tmp[1]);
}
return 0;
}
//0x88c30106 0xbdc2acc3 0xaec2b8c2 0xc313a2c2 0xc3bcc3a2 0xc32807ab 0x87c2438f 0x9dc394c2 0x95c26c54 0xa1c385c3 0x5d86c253 0xc2bcc378

端序问题转换一下,提取出数据0x6,0x1,0xc3,0x88,0xc3,0xac,0xc2,0xbd,0xc2,0xb8,0xc2,0xae,0xc2,0xa2,0x13,0xc3,0xa2,0xc3,0xbc,0xc3,0xab,0x7,0x28,0xc3,0x8f,0x43,0xc2,0x87,0xc2,0x94,0xc3,0x9d,0x54,0x6c,0xc2,0x95,0xc3,0x85,0xc3,0xa1,0x53,0xc2,0x86,0x5d,0x78,0xc3,0xbc,0xc2
因为在java字符串传递到jni接口时,采用了GetStringUtfChars,这个函数会自动采用utf-8的解码格式。
我们需要将提取到的utf-8编码的数据转化为对应的Unicode编码

UTF-8编码,是Unicode的一种可变长度字符编码,是Unicode的一种实现方式,又称万国码;UTF8使用1~4字节为每个字符编码,相对于Unicode 固定的四字节长度,更节省存储空间。UTF-8字节长度与Unicode 码点对应关系如下:
一字节(0x00-0x7F)-> U+00~U+7F
二字节(0xC280-0xDFBF)-> U+80~U+7FF
三字节(0xE0A080-0xEFBFBF)-> U+800~U+FFFF
四字节(0xF0908080-0xF48FBFBF)-> U+10000~U+10FFFF
字符U+0000到U+007F(ASCII)被编码为字节0×00到0x7F(ASCIⅡ兼容)。这意味着只包含7位ASCIl字符的文件在ASCIⅡ和UTF-8两种编码方式下是一样的。
所有大于0x007F的字符被编码为一个有多个字节的串,每个字节都有标记位集,常用汉字基本上都被编码成三字节。

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
data = [0x6,0x1,0xc3,0x88,0xc3,0xac,0xc2,0xbd,0xc2,0xb8,0xc2,0xae,0xc2,0xa2,0x13,0xc3,0xa2,0xc3,0xbc,0xc3,0xab,0x7,0x28,0xc3,0x8f,0x43,0xc2,0x87,0xc2,0x94,0xc3,0x9d,0x54,0x6c,0xc2,0x95,0xc3,0x85,0xc3,0xa1,0x53,0xc2,0x86,0x5d,0x78,0xc3,0xbc,0xc2]
i = 0
result = []
while i < len(data):
byte = data[i]
if byte == 0xc0 and i+1 < len(data) and data[i+1] == 0x80:
result.append(0x00)
i += 2
elif 0x00 <= byte <= 0x7F:
result.append(byte)
i += 1
elif 0xC0 <= byte <= 0xDF and i+1 < len(data):
char = bytes([data[i], data[i+1]])
decoded = char.decode('utf-8', errors='ignore')
if decoded:
result.append(ord(decoded))
else:
result.append(byte)
i += 2
elif 0xE0 <= byte <= 0xEF and i+2 < len(data):
char = bytes([data[i], data[i+1], data[i+2]])
decoded = char.decode('utf-8', errors='ignore')
if decoded:
result.append(ord(decoded))
else:
result.append(byte)
i += 3
else:
result.append(byte)
i += 1
print(result)
#[6, 1, 200, 236, 189, 184, 174, 162, 19, 226, 252, 235, 7, 40, 207, 67, 135, 148, 221, 84, 108, 149, 197, 225, 83, 134, 93, 120, 252, 194]

由于出题人代码写胡了,少了最后几位[168,56,93],正确的转换结果应该为[6, 1, 200, 236, 189, 184, 174, 162, 19, 226, 252, 235, 7, 40, 207, 67, 135, 148, 221, 84, 108, 149, 197, 225, 83, 134, 93, 120, 252, 168, 56, 93]
然后解一下RC4

exp

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
from Crypto.Cipher import ARC4
data = [0x6,0x1,0xc3,0x88,0xc3,0xac,0xc2,0xbd,0xc2,0xb8,0xc2,0xae,0xc2,0xa2,0x13,0xc3,0xa2,0xc3,0xbc,0xc3,0xab,0x7,0x28,0xc3,0x8f,0x43,0xc2,0x87,0xc2,0x94,0xc3,0x9d,0x54,0x6c,0xc2,0x95,0xc3,0x85,0xc3,0xa1,0x53,0xc2,0x86,0x5d,0x78,0xc3,0xbc,0xc2]
i = 0
result = []
while i < len(data):
byte = data[i]
if byte == 0xc0 and i+1 < len(data) and data[i+1] == 0x80:
result.append(0x00)
i += 2
elif 0x00 <= byte <= 0x7F:
result.append(byte)
i += 1
elif 0xC0 <= byte <= 0xDF and i+1 < len(data):
char = bytes([data[i], data[i+1]])
decoded = char.decode('utf-8', errors='ignore')
if decoded:
result.append(ord(decoded))
else:
result.append(byte)
i += 2
elif 0xE0 <= byte <= 0xEF and i+2 < len(data):
char = bytes([data[i], data[i+1], data[i+2]])
decoded = char.decode('utf-8', errors='ignore')
if decoded:
result.append(ord(decoded))
else:
result.append(byte)
i += 3
else:
result.append(byte)
i += 1
# print(result)
a = [6, 1, 200, 236, 189, 184, 174, 162, 19, 226, 252, 235, 7, 40, 207, 67, 135, 148, 221, 84, 108, 149, 197, 225, 83, 134, 93, 120, 252, 168, 56, 93]
key = b'friedchick'
enc = b''.join([bytes([i]) for i in a])
rc4 = ARC4.new(key)
decrypted_data = rc4.decrypt(enc)
print(decrypted_data.decode())
# flag{G0ddamn700h@rd_f0ru_n0w!!!}

flag

flag{G0ddamn700h@rd_f0ru_n0w!!!}