直接打开,看到flag,但是是小端存储要倒过来
也可以直接在hex中查看,中间不需要的部分删除
得到flag:HZNUCTF{We1c0me_t0_Reverse}
下载附件可以看到如下,这个好像是python的字节码(python -m dis xxx.py可以看到字节码)
0 LOAD_GLOBAL 0 (input)
2 LOAD_CONST 1 ('plz input your flag:')
4 CALL_FUNCTION 1
6 STORE_FAST 0 (a)//值a开始
8 LOAD_CONST 2 (25)
10 LOAD_CONST 3 (108)
12 LOAD_CONST 3 (108)
14 LOAD_CONST 4 (176)
16 LOAD_CONST 5 (18)
18 LOAD_CONST 3 (108)
20 LOAD_CONST 6 (110)
22 LOAD_CONST 7 (177)
24 LOAD_CONST 8 (64)
26 LOAD_CONST 9 (29)
28 LOAD_CONST 10 (134)
30 LOAD_CONST 9 (29)
32 LOAD_CONST 11 (187)
34 LOAD_CONST 12 (103)
36 LOAD_CONST 13 (32)
38 LOAD_CONST 14 (139)
40 LOAD_CONST 15 (144)
42 LOAD_CONST 16 (179)
44 LOAD_CONST 10 (134)
46 LOAD_CONST 7 (177)
48 LOAD_CONST 13 (32)
50 LOAD_CONST 17 (24)
52 LOAD_CONST 15 (144)
54 LOAD_CONST 2 (25)
56 LOAD_CONST 18 (111)
58 LOAD_CONST 19 (14)
60 LOAD_CONST 18 (111)
62 LOAD_CONST 19 (14)//结束
64 BUILD_LIST 28
66 STORE_FAST 1 (c)
68 LOAD_GLOBAL 1 (len)
70 LOAD_FAST 0 (a)
72 CALL_FUNCTION 1
74 LOAD_CONST 20 (28)
76 COMPARE_OP 3 (!=)//判断a的长度是否等于28
78 POP_JUMP_IF_FALSE 92
80 LOAD_GLOBAL 2 (print)
82 LOAD_CONST 21 ('wrong length')
84 CALL_FUNCTION 1
86 POP_TOP
88 LOAD_CONST 22 (0)
90 RETURN_VALUE
92 LOAD_GLOBAL 3 (range)//做一个循环,在0到28范围内
94 LOAD_GLOBAL 1 (len)
96 LOAD_FAST 0 (a)
98 CALL_FUNCTION 1
100 CALL_FUNCTION 1
102 GET_ITER
104 FOR_ITER 46 (to 152)
106 STORE_FAST 2 (i)
108 LOAD_GLOBAL 4 (ord)
110 LOAD_FAST 0 (a)
112 LOAD_FAST 2 (i)
114 BINARY_SUBSCR
116 CALL_FUNCTION 1
118 LOAD_CONST 23 (39)//先乘39再对196取模
120 BINARY_MULTIPLY//乘法
122 LOAD_CONST 24 (196)
124 BINARY_MODULO//取模
126 LOAD_FAST 1 (c)
128 LOAD_FAST 2 (i)
130 BINARY_SUBSCR
132 COMPARE_OP 3 (!=)//判断c是否与a相等,相等即正确
134 POP_JUMP_IF_FALSE 104
136 LOAD_GLOBAL 2 (print)
138 LOAD_CONST 25 ('wrong')
140 CALL_FUNCTION 1
142 POP_TOP
144 POP_TOP
146 LOAD_CONST 0 (None)
148 RETURN_VALUE
150 JUMP_ABSOLUTE 104
152 LOAD_GLOBAL 2 (print)
154 LOAD_CONST 26 ('win')
156 CALL_FUNCTION 1
158 POP_TOP
160 LOAD_CONST 0 (None)
162 RETURN_VALUE
分析完代码,来写脚本爆破
#include
#include
int main() {
int a[28];
int i=0,j=0;
int c[28] = {25, 108, 108, 176, 18, 108, 110, 177, 64, 29, 134, 29, 187, 103, 32, 139, 144, 179, 134, 177, 32, 24, 144, 25, 111, 14, 111, 14};
for(j=0;j<=28;j++){
for(i=0;i<222;i++){
if(i * 39 % 196 == c[j]){
a[j]=i;
}
}
}for(i=0;i<28;i++){
printf("%c",a[i]);
}return 0;
}
直接爆出flag :flag{G00dj0&_H3r3-I$Y@Ur_$L@G!~!~},这个非常奇怪,i循环的范围太大了(0~256)就会乱码,显示错误的,太小了(0~122)之间flag中~符号就不会显现,搞了很久才发现这个问题。
这题是个安卓逆向的题,打开通过.xml文件可以找到主函数登录页面,猜测flag就在这里
往下找可以看到加密方式为AES/CBC/PKCS5Padding加密,具体内容可以看一位师傅的博客(https://blog.csdn.net/wangmx1993328/article/details/106170060)
可以看到这段代码,是进行AES/CBC/PKCS5Padding解密的过程,传进decrypt函数的参数ciphertext是加密后再用base64加密的密文,key.getBytes()是加密密钥,因为AES加密是对称加密,即加密和解密的密钥要一致,“iviviviviviv".getBytes()是加密的偏移量,注意密钥key和偏移量iv必须是AES加密是必须是16位,且iv通常不为中文,现在就来找key和iv,key值看到在这
直接文本搜索
可以看到选中的这一行
kkkeyyy404404404
注意啊key不是这个,当时一度被这个迷惑了...
分析完解密过程后可以尝试2种解密方法
一,在线工具解密(在线AES加密解密高级版-ME2在线工具)
二,解密脚本
import base64
from Crypto.Cipher import AES
def AES_Decrypt(key, data):
vi = 'iviviviviviviviv'
data = data.encode('utf8')
encodebytes = base64.decodebytes(data)
# 将加密数据转换位bytes类型数据
cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
text_decrypted = cipher.decrypt(encodebytes)
unpad = lambda s: s[0:-s[-1]]
text_decrypted = unpad(text_decrypted)
# 去补位
text_decrypted = text_decrypted.decode('utf8')
return text_decrypted
key = 'kkkeyyy404404404' #自己密钥
data = 'Lz49p2OjPZzUMXakynHQuw==' #需要加密的内容
text_decrypted = AES_Decrypt(key, data)
print(text_decrypted)
#reiseasy
最后在真机或者虚拟机上运行
题目提示要Base58加密,得到flag:
注意:要打开magic功能(https://gchq.github.io/CyberChef/放上解密工具)
打开文件,拖入到ida中
可以看到部分伪代码
但是后面的代码无法查看,到汇编窗口看看,
可以看到这里有一大段数据,
可以看到004011EA这里跳到了loc_401200这个地方,loc_401200这里调用了loc_401206这个地方,中间的db 00E8h是没有任何用处的,实际上loc_401206这里也没有什么用处,都是花指令。全部nop掉。
实际上前面nop掉后,后面的代码会自动解析,接着f5分析,可以得到完整代码。通过代码可以看出来是rc4加密算法,但是改了一点,比rc4算法多了一个异或0x37(加密过程有很多%256的,要考虑一下是不是rc4)
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+0h] [ebp-DF4h]
char v5; // [esp+0h] [ebp-DF4h]
char v6; // [esp+0h] [ebp-DF4h]
int v7[604]; // [esp+Ch] [ebp-DE8h]
int v8; // [esp+97Ch] [ebp-478h]
int v9; // [esp+980h] [ebp-474h]
_BYTE *v10; // [esp+984h] [ebp-470h]
int v11; // [esp+988h] [ebp-46Ch]
int v12; // [esp+98Ch] [ebp-468h]
char *v13; // [esp+990h] [ebp-464h]
_BYTE *v14; // [esp+994h] [ebp-460h]
unsigned int m; // [esp+998h] [ebp-45Ch]
int v16; // [esp+99Ch] [ebp-458h]
unsigned int k; // [esp+9A0h] [ebp-454h]
int j; // [esp+9A4h] [ebp-450h]
char *v19; // [esp+9A8h] [ebp-44Ch]
char *p_Arglist; // [esp+9ACh] [ebp-448h]
char *v21; // [esp+9B0h] [ebp-444h]
_BYTE *v22; // [esp+9B4h] [ebp-440h]
int i; // [esp+9B8h] [ebp-43Ch]
int v24; // [esp+9BCh] [ebp-438h]
int v25; // [esp+9C4h] [ebp-430h]
int v26; // [esp+9C8h] [ebp-42Ch]
char Arglist; // [esp+9CCh] [ebp-428h] BYREF
_BYTE v28[1023]; // [esp+9CDh] [ebp-427h] BYREF
int v29[3]; // [esp+DCCh] [ebp-28h] BYREF
char v30; // [esp+DD8h] [ebp-1Ch] BYREF
char v31[23]; // [esp+DD9h] [ebp-1Bh] BYREF
v7[601] = 9;
v7[600] = 0;
v26 = 0;
qmemcpy(v29, "tellmewhy", 9); // 密匙
v30 = 2;
qmemcpy(v31, "9+", 2);
v31[2] = -84;
v31[3] = -44;
v31[4] = 120;
v31[5] = -82;
v31[6] = -93;
v31[7] = 66;
v31[8] = 58;
v31[9] = 17;
v31[10] = -7;
v31[11] = 90;
v31[12] = -55;
v31[13] = -68;
v31[14] = -84;
v31[15] = 22;
v31[16] = -80;
v31[17] = -57;
v31[18] = 85;
v31[19] = -72;
v31[20] = 99;
v31[21] = 0x80;
printf(Format, v4);
sub_401050("%s", &Arglist);
v14 = v28;
v22 = &v28[strlen(&Arglist)];
v12 = v22 - v28;
v13 = v31;
v21 = &v31[strlen(&v30)];
v11 = v21 - v31;
if ( v22 - v28 == v21 - v31 )
{
while ( v24 < 256 )
{
v7[v24] = *(v29 + v24 % 9);
v7[v24 + 300] = v24;
++v24;
}
for ( i = 0; i < 256; ++i )
{
v26 = (v7[i] + v7[i + 300] + v26) % 256;
v16 = v7[i + 300];
v7[i + 300] = v7[v26 + 300];
v7[v26 + 300] = v16 ^ 0x37;//多的异或过程
}
for ( j = 0; j < 256; ++j )
;
printf("\n", v5);
v25 = 0; // 设置两个变量
v26 = 0;
for ( k = 0; ; ++k )
{
p_Arglist = &Arglist;
v10 = v28;
p_Arglist += strlen(p_Arglist);
v9 = ++p_Arglist - v28;
if ( k >= p_Arglist - v28 ) // 生成密匙流
break;
v25 = (v25 + 1) % 256;
v26 = (v7[v25 + 300] + v26) % 256;
v16 = v7[v25 + 300];
v7[v25 + 300] = v7[v26 + 300]; // 交换
v7[v26 + 300] = v16;
v8 = (v7[v26 + 300] + v7[v25 + 300]) % 256;
*(&v26 + v25 + 3) ^= LOBYTE(v7[v8 + 300]);// 加密过程(异或)
}
for ( m = 0; ; ++m )
{
v19 = &Arglist;
v7[603] = v28;
v19 += strlen(v19);
v7[602] = ++v19 - v28;
if ( m >= v19 - v28 )
break;
if ( v28[v25 - 1] != v31[v25 - 1] ) // 最后密文要等于v31
{
printf("Maybe you should think about it", v6);
return 0;
}
}
printf("Wow,tqllll!", v6);
return 0;
}
else
{
printf("Tooooo short!", v5);
return 0;
}
}
写解密脚本
int main() {
// int i, m, n;
int key_len = 9;//密钥长度;
int buf_len = 0;//明文长度
int j=0, q, n;
int b[300];
int s[300];
unsigned char key_data[9] = "tellmewhy";
unsigned char buf_data[] = { 0x02,0x39,0x2b,0xac,0xd4,0x78,0xae,0xa3,0x42,0x3a,0x11,0xf9,0x5a,0xc9,0xbc,0xac,0x16,0xb0,0xc7,0x55,0xb8,0x63,0x80 };
for (int i = 0; i < 256; i++) {
b[i] = key_data[i % 9]; //8是密钥的长度 (变)
s[i] = i;
}
for (int i = 0; i < 256; i++) {
j = (j + s[i] + b[i]) % 256;
q = s[i];
s[i] = s[j];
s[j] = q ^ 0x37;
}
int i = 0, t;
j = 0;
for (int w = 0; w < strlen(buf_data); w++) { //32是data的长度 (变)
i = (i + 1) % 256;
j = (j + s[i]) % 256;
q = s[i];
s[i] = s[j];
s[j] = q; //交换
t = (s[i] + s[j]) % 256;
buf_data[i - 1] ^= s[t];//s[t]是最后的密钥
}
printf("%s\\n",buf_data);
return 0;
}
再贴一个脚本,可以适用于没被魔改的rc4算法
import codecs
def rc4_decrypt(key, ciphertext):
S = list(range(256))
j = 0
out = []
# KSA Phase
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
# PRGA Phase
i = j = 0
for char in ciphertext:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
out.append(chr(char ^ S[(S[i] + S[j])%256]))
return ''.join(out)
key = b''#密钥
ciphertext = codecs.decode('', 'hex')#密文
plaintext = rc4_decrypt(key, ciphertext)
print(plaintext)
1.什么是python字节码,如何处理pytho字节码
2.apk文件,放入jadx中,并且通过文本搜索找到相关信息,通过.xml文件可以找到主函数登录页面,查看相关函数,还可以直接下载在手机上进一步操作。
3。rc4算法原理和解密脚本。
4.注意:nop的机器码是90,可以把数据改成90,实现nop,但是在代码段中数据的机器码被改成90后还要按c识别为代码,否则5无法重新识别
5.常见的花指令机器码要熟悉:call:E8 jmp:E9 jmp short:EB