开始
一般来说,我们拿到一个ctf的题,就是为了得到它的flag,为了它,RE做安全的十分努力,百分艰辛,就为了得到它。
流程
试运行
有了ctf的re的题目,ctfer有一套经典的流程,双击打开xxx.exe开始你的find flag之旅,一般来说就会提示
,它的意思是说,你好,CTFer,输入你的flag,那么你就可以输入,不用说,几乎都是you are wrong
所以,为了不错,得到正确的答案flag,就必须选择理解它,信任它。它自然会给你答案。
反编译
相信它,自然就是把它扔进ida,进行逆向分析。下面给出简单的几个exe的ctf题。
T1
你将会得到
看着T1的伪代码,一眼就可以看出这个题的flag,
1 | flag{YOU_FIND_IT} |
再执行流程,双击打开T1.exe,输入你的flag,就可以得到you are right,就这么简单。
T2
拿道题流程完毕,ida,就又可以得到
可以看出,用户输入一个str1的字符串,然后利用for循环进行加i操作,最后将新得到的str1和“gmbh|ZPV`GJOE`JU`IBIB~”进行比较,如果相同,就是you are right,不同就是you are wrong。
思路: 对“gmbh|ZPV
GJOEJU`IBIB~”进行逐个减去i,即可得到答案。解题:
可以用C语言写一个脚本1
2
3
4
5
6
7
8
9
10#include <stdio.h>
#include <string.h>
int main(){
char a[50];
scanf("%s",a);
for(int i=0;i<strlen(a);++i){
--a[i];
}
printf("%s-%d",a,strlen(a));
}输入”gmbh|ZPV
GJOEJU`IBIB~”得到答案
T3
拿题流程完毕,ida可以得到
对已知的函数命名,可以看出,T3是对T2的一次升级,加入了异或的算法。
题目: 用scanf输入str1 -> 在 for 中 将str1的每一个字符与其下标异或 -> 将得到的str1与 str2比较
知识点:异或运算是一个二元运算,在C语言中的运算符号为 ^,规则为
1
2
3A ^ B = C
C ^ B = A
C ^ A = B也就是说:
1
2明文 ^ 密钥 = 密文
密文 ^ 密钥 = 明文所以,解题就是再对str2进行一次异或运算
str2
双击str2会跳转到它的数据定义窗口
,从’66h’,’6Dh’到’55h’,’68h’,’0’的字符串数据,可以通过LazyIDA插件转换为byte字节。str2的实际数据是1
[0x66, 0x6D, 0x63, 0x64, 0x7F, 0x5C, 0x49, 0x52, 0x57, 0x4F, 0x43, 0x45, 0x48, 0x52, 0x47, 0x5B, 0x4F, 0x59, 0x53, 0x5B, 0x55, 0x68, 0x00]
解题
C语言的脚本1
2
3
4
5
6
7
8
9
10#include <stdio.h>
#include <string.h>
int main(){
char a[]={0x66, 0x6D, 0x63, 0x64, 0x7F, 0x5C, 0x49, 0x52,0x57, 0x4F, 0x43, 0x45, 0x48, 0x52, 0x47, 0x5B, 0x4F, 0x59, 0x53, 0x5B, 0x55, 0x68, 0x00};
for(int i=0;i<strlen(a);++i){
a[i]^=i;
}
printf("%s",a);
return 0;
}运行就可以得到flag
T4
放进ida之后,可以得到
T4.exe在T3.exe基础上加上了一个新的函数处理sub_455A94( )
题目:输入str -> 对str的字符串进行异或 -> 再对整个字符串sub_455A94( )处理 ->与 ”Zm1jZH9cSVJXT0NFSFJHW09ZU1tVaA==”比较
sub_455A94
这是一个啥函数,不知道。但是我们可以了解。双击函数看到
再次双击函数sub_45A3F0,这是对sub_45A3F0 的封装,进入底层函数,
该函数多次引用off_529000 ,双击可以跳转到其资源地址,可以把鼠标放在图示位置
可以看到是字符表,推敲为base64,所以sub_455A94()为base64加密处理解题思路: 对“Zm1jZH9cSVJXT0NFSFJHW09ZU1tVaA==”进行base64解码 -> 再进行异或解密 -> flag
解题代码 (C语言)
1
2
3
4
5
6
7
8
9
10#include <stdio.h>
#include <string.h>
int main(){
char a[]="fmcd\IRWOCEHRG[OYS[Uh";//对“Zm1jZH9cSVJXT0NFSFJHW09ZU1tVaA==”base64解密后的字符串
for(int i=0;i<strlen(a);++i){
a[i]^=i;
}
printf("%s",a);
return 0;
}
T5
直接得到如下
Amazing,T5和T4竟然差不多,直接按照T4的方法得到flag,你就会得到”you are wrong”
这是因为T5和T4还是有不同之处的。
不同之处:在sub_455A94()函数中,依旧是sub_45A3F0(),也使用了off_529000 ,不过内部,并没有使用标准字母表,而是图示
这属于魔改字母表。base64
base64一般是使用的标准字母表计算出来的字符串
1
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
但是,标准字母表的字母位置和改动了的字母位置下标应该一样。
替换base64:在表中应该如下替换
1
2
3
4待替换 Wj1gWE9xPSGUQ0KCPCGET09WR1qSzZ
魔改表 ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/
标准表 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
已替换 Zm1jZH9cSVJXT0NFSFJHW09ZU1tVaA也可以写一个Python脚本替换
1
2
3
4
5
6
7
8import base64;
v="Wj1gWE9xPSGUQ0KCPCGET09WR1qSzZ";
a="ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/";
b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
r="";
for i in v:
r+=b[a.find(i)];
print(r); #结果是Zm1jZH9cSVJXT0NFSFJHW09ZU1tVaA替换完成后,就可以使用标准的base64进行解码
C语言解题
1
2
3
4
5
6
7
8
9
10#include <stdio.h>
#include <string.h>
int main(){
char a[]="fmcd\IRWOCEHRG[OYS[Uh" ;//base64解码后的结果
for(int i=0;i<strlen(a);++i){
a[i]^=i;
}
printf("%s",a);
return 0;
}
T6
直接IDA,得到
看到伪代码一大堆,不过,也不用怕,可以看到str2为用户输入,将str2和str1作比较,str1是通过一系列代码运算出来的,这么复杂,怎么办?其实很简单,既然是计算出来的,那就可以把计算表达式抄下来,写一个C程序,把str1用printf出来即可。
不过,IDA这么高级,当然也可以动态调试。
- 解题:
在ida的菜单栏里面找到debugger->select debugger
选择local windows debugger
在代码行的前面标记小蓝点,就表示在这里需要运行时停下来调试
在菜单栏->debugger->start progress开始调试运行了
后面会跳出命令提示框,随便输入点东西,回车继续运行,停止后,将鼠标移动到str1上面,就可以看见程序运行时的结果了。
你的flag就是它了。
完整的文件下载
本文如有不对,请指正