一步一个脚印地耐心攀登,就是别去看顶峰,而要专注于在爬的路。
——黑泽明
目录
1.查壳
编辑
64bit exe文件
2.IDA静态分析main函数
拖入IDA,找到main函数
int __cdecl main(int argc, const char **argv, const char **envp) { int v3; // eax int v4; // eax int v5; // eax int result; // eax char Str; // [rsp+20h] [rbp-60h] char Str1; // [rsp+50h] [rbp-30h] char v9; // [rsp+90h] [rbp+10h] char v10; // [rsp+D0h] [rbp+50h] char Str2[8]; // [rsp+110h] [rbp+90h] int v12; // [rsp+14Ch] [rbp+CCh] _main(); strcpy(Str2, "EmBmP5Pmn7QcPU4gLYKv5QcMmB3PWHcP5YkPq3=cT6QckkPckoRG"); puts("Hello, please input your flag and I will tell you whether it is right or not."); scanf("%38s", &Str); if ( strlen(&Str) == 38 && (v3 = strlen(&Str), (unsigned int)encode_one(&Str, v3, &v10, &v12) == 0) && (v4 = strlen(&v10), (unsigned int)encode_two(&v10, v4, &v9, &v12) == 0) && (v5 = strlen(&v9), (unsigned int)encode_three(&v9, v5, &Str1, &v12) == 0) && !strcmp(&Str1, Str2) ) { puts("you are right!"); result = 0; } else { printf("Something wrong. Keep going."); result = 0; } return result; }
我们将flag输入到了str
编辑
if ( strlen(&Str) == 38
&& (v3 = strlen(&Str), (unsigned int)encode_one(&Str, v3, &v10, &v12) == 0)
&& (v4 = strlen(&v10), (unsigned int)encode_two(&v10, v4, &v9, &v12) == 0)
&& (v5 = strlen(&v9), (unsigned int)encode_three(&v9, v5, &Str1, &v12) == 0)
&& !strcmp(&Str1, Str2) )
可以看出正确的flag长度应该是38
根据函数名应该是有三次加密
看到第一次加密encode_one(&Str, v3, &v10, &v12) ,v10是引用的形式传参,v4 = strlen(&v10), (unsigned int)encode_two(&v10, v4, &v9, &v12) == 0,第二次在传str的地方传入了v10,第三次则是v9,可以猜测应该是对输入的str三重加密
最后对比str1,str2,相等,并且前面的条件都正确,就是正确的flag
str1,str2跟进
编辑
编辑
dup即英文duplicate的缩写,重复的意思,用来定义重复的字节、字、双字、结构等内存缓冲区。
?表示值是未定义的 汇编语言
编辑
说明str1,str2最初没有什么内容
emmm,不对
编辑
在主函数开始的时候,给Str2赋值了,
重点研究三重加密是什么
3.研究三重加密
第一重加密
__int64 __fastcall encode_one(const char *a1, int a2, char *a3, int *a4) { int v5; // esi int v6; // esi int v7; // esi int v8; // [rsp+34h] [rbp-1Ch] int v9; // [rsp+38h] [rbp-18h] char *v10; // [rsp+40h] [rbp-10h] int v11; // [rsp+48h] [rbp-8h] int i; // [rsp+4Ch] [rbp-4h] unsigned __int8 *v13; // [rsp+70h] [rbp+20h] int v14; // [rsp+78h] [rbp+28h] int *v15; // [rsp+88h] [rbp+38h] v13 = (unsigned __int8 *)a1; v14 = a2; v15 = a4; if ( !a1 || !a2 ) return 0xFFFFFFFFi64; v11 = 0; if ( a2 % 3 ) v11 = 3 - a2 % 3; v9 = a2 + v11; v8 = 8 * (a2 + v11) / 6; v10 = a3; for ( i = 0; i < v9; i += 3 ) { *v10 = alphabet[(char)*v13 >> 2]; if ( v14 + v11 - 3 == i && v11 ) { if ( v11 == 1 ) { v5 = (char)cmove_bits(*v13, 6u, 2u); v10[1] = alphabet[v5 + (char)cmove_bits(v13[1], 0, 4u)]; v10[2] = alphabet[(char)cmove_bits(v13[1], 4u, 2u)]; v10[3] = 61; } else if ( v11 == 2 ) { v10[1] = alphabet[(char)cmove_bits(*v13, 6u, 2u)]; v10[2] = 61; v10[3] = 61; } } else { v6 = (char)cmove_bits(*v13, 6u, 2u); v10[1] = alphabet[v6 + (char)cmove_bits(v13[1], 0, 4u)]; v7 = (char)cmove_bits(v13[1], 4u, 2u); v10[2] = alphabet[v7 + (char)cmove_bits(v13[2], 0, 6u)]; v10[3] = alphabet[v13[2] & 0x3F]; } v10 += 4; v13 += 3; } if ( v15 ) *v15 = v8; return 0i64; }
我们只需要看这个加密算法的核心部分,根据加密算法的特征,识别出什么加密算法
move_bit字面意思和使用这个函数的次数很像是base64,看一下加密算法中很显眼的这个alphabet数组
编辑
h是十六进制的意思,hex,按下快捷键a转换成字符看一下
编辑
发现确实应该是一个base64加密表
第二重加密
__int64 __fastcall encode_two(const char *a1, int a2, char *a3, int *a4) { char *Source; // [rsp+40h] [rbp+10h] char *v6; // [rsp+50h] [rbp+20h] Source = (char *)a1; v6 = a3; if ( !a1 || !a2 ) return 0xFFFFFFFFi64; strncpy(a3, a1 + 26, 0xDui64); strncpy(v6 + 13, Source, 0xDui64); strncpy(v6 + 26, Source + 39, 0xDui64); strncpy(v6 + 39, Source + 13, 0xDui64); return 0i64; }
encode_two(&v10, v4, &v9, &v12) == 0
根据前面的推断,第三个参数应该是加密后的密文,用一个Source指针指向原来的明文,然后进行strncpy操作
strncpy(a3, a1 + 26, 0xDui64);
strncpy(v6 + 13, Source, 0xDui64);
strncpy(v6 + 26, Source + 39, 0xDui64);
strncpy(v6 + 39, Source + 13, 0xDui64);
就是一个简单的替换加密
第三重加密
__int64 __fastcall encode_three(const char *a1, int a2, char *a3, int *a4) { char v5; // [rsp+Fh] [rbp-11h] int i; // [rsp+14h] [rbp-Ch] char *v7; // [rsp+18h] [rbp-8h] const char *v8; // [rsp+30h] [rbp+10h] v8 = a1; if ( !a1 || !a2 ) return 0xFFFFFFFFi64; v7 = a3; for ( i = 0; i < a2; ++i ) { v5 = *v8; if ( *v8 <= 64 || v5 > 90 ) { if ( v5 <= 96 || v5 > 122 ) { if ( v5 <= 47 || v5 > 57 ) *v7 = v5; else *v7 = (v5 - 48 + 3) % 10 + 48; } else { *v7 = (v5 - 97 + 3) % 26 + 97; } } else { *v7 = (v5 - 65 + 3) % 26 + 65; } ++v7; ++v8; } return 0i64; }
第三重加密是凯撒加密
*v7 = (v5 - 48 + 3) % 10 + 48;这是典型的凯撒加密的特征
不了解凯撒加密阅读 凯撒加密
需要简单熟悉一下ASCLL码表,便于阅读
使用快捷键r改为字符,可以看出分别对于大小写字母和数字进行了移动三位的凯撒加密
编辑
分析完毕,开始爆破
编辑
4.解密
!strcmp(&Str1, Str2)
三重加密之后的字符串是Str1,Str2是一直的,当Str1等于Str2的时候会输出"you are right!"
我们就需要将Str2的值逆向三重解密,得到正确的flag
需要解密的密文EmBmP5Pmn7QcPU4gLYKv5QcMmB3PWHcP5YkPq3=cT6QckkPckoRG
第一重解密(凯撒加密的解密)
def decrypt_caesar(str, key=3): # 解密函数 text = "" for i in str: if ord(i) >= 65 and ord(i) <= 90: text += chr(65 + ((ord(i) - 65) - key) % 26) elif ord(i) >= 97 and ord(i) <= 122: text += chr(97 + ((ord(i) - 97) - key) % 26) elif ord(i) >= 48 and ord(i) <= 57: text += chr(48 + ((ord(i) - 48) - key) % 10) else: text+=i return text m1="EmBmP5Pmn7QcPU4gLYKv5QcMmB3PWHcP5YkPq3=cT6QckkPckoRG" m2=decrypt_caesar(m1) print(m2)
脚本运行结果
BjYjM2Mjk4NzMR1dIVHs2NzJjY0MTEzM2VhMn0=zQ3NzhhMzhlOD
第二重解密(替换加密)
就是一个四部分替换一下顺序,不写脚本了,简单手推
BjYjM2Mjk4NzM 3 R1dIVHs2NzJjY 1 0MTEzM2VhMn0= 4 zQ3NzhhMzhlOD 2 R1dIVHs2NzJjYzQ3NzhhMzhlODBjYjM2Mjk4NzM0MTEzM2VhMn0=
破解结果
R1dIVHs2NzJjYzQ3NzhhMzhlODBjYjM2Mjk4NzM0MTEzM2VhMn0=
第三重解密(base64解密)
import base64 a="R1dIVHs2NzJjYzQ3NzhhMzhlODBjYjM2Mjk4NzM0MTEzM2VhMn0=" b=base64.b64decode(a.encode()); print(b.decode())
也可以使用在线解密网站
GWHT{672cc4778a38e80cb362987341133ea2}
flag{672cc4778a38e80cb362987341133ea2}
边解题编写题解,别的buuctf逆向题解可以关注我的专栏reverse
编辑