第一题 re1.exe
0x01.运行程序
可以看到需要输入正确的flag
那么现在,我们需要判断程序是多少位的,有没有加壳
0x02.exeinfope查详细信息
可以看到程序是32位的,是Microsoft Visual c++编译的,并且没有加壳
注:查壳工具还有PEID,EID,但是推荐EID或者exeinfope,因为,PEID查壳的时候有时候不准确
那么,我们可以用静态分析神器 IDA 打开,进一步分析了
0x03.
然后,查找主函数main,可以看到右侧的是反汇编的汇编代码,这时候,我们可以直接分析汇编语言,但是,汇编语言看起来太多,费劲。这个时候就可以是有IDA是最强大的功能F5了,它能够直接将汇编代码生成C语言代码,虽然和这个程序的源码不完全一样,但是逻辑关系是一样的
F5查看伪代码
这是整个main函数的运算逻辑
可以看到一个关键的字符串,print(aFlag),那么证明这就是输入正确flag,然后,会输出aFlag证明你的flag正确,然后,继续往上分析,可以看到v3的值,是由strcmp()决定的,比较v5和输入的字符串,如果一样就会进入后面的if判断,所以,我们继续往上分析,看看哪里又涉及v5,可以看到开头的_mm_storeu_si128(),对其进行分析发现它类似于memset(),将xmmword_413E34的值赋值给v5,所以,我们可以得到正确的flag应该在xmmword_413E34中,然后,我们双击413E34进行跟进
可以看到一堆十六进制的数
这时,我们使用IDA的另一个功能 R ,能够将十进制的数转换为字符串。
这就是我们最后的flag了
注:这里要跟大家普及一个知识了,及大端与小端
假设一个十六进制数0x12345678
大端的存储方式是:12,34,56,78,然后读取的时候也是从前往后读
小端的存储方式是:78,56,34,12,然后读取的时候是从后往前读取
所以,最后的flag应该是:DUTCTF{We1c0met0DUTCTF}
0x04.运行程序输入正确的flag
DUTCTF{We1c0met0DUTCTF}
第二题 game.exe
0x01.运行程序
可以看到程序应该是输入正确顺序使八个图案都变亮
0x02.查看详细信息
我们直接使用神器IDA
0x04.IDA
F5查看伪代码
然后,我们跟进Main函数
可以看到这是整个函数的运算逻辑
先是判断是输入的是否是1-8,然后进入后面的if判断然后进行循环,这个时候应该就是程序的亮暗的显示,然后,如果byte_532E28每一位都是1,那么,就会进入sub_457AB4,然后我们猜测这里应该就是最后的flag的地方。
然后我们跟进 sub_457AB4
注:这里说明一下,如果IDA不能正确的获得自定义函数的名字,那么IDA会用sub__加上自定义函数的起始地址来定义函数的名字
这里只截取了后面的部分,发现函数进行了两次xor运算,xor的逆运算也是xor,那么我们就可以根据这个运算来写脚本得到最后的flag
这里看到v2和v59这就证明了这是两个数组的运算,所以我们应该将上面的字符串分成两个数组,分别从v2和v59开始
0x05.写EXP
这里先是通过循环,将a和b数组的值进行xor运算,然后再将数组a的值与0x13xor运算
chr():是将十六进制转换为字符串
0x05.运行脚本
#coding:utf-8
b=[18, 64, 98, 5, 2, 4, 6, 3, 6, 48, 49, 65, 32, 12, 48, 65, 31, 78, 62, 32, 49, 32, 1, 57, 96, 3, 21, 9, 4, 62, 3, 5, 4, 1, 2, 3, 44, 65, 78, 32, 16, 97, 54, 16, 44, 52, 32, 64, 89, 45, 32, 65, 15, 34, 18, 16, 0]
a=[123, 32, 18, 98, 119, 108, 65, 41, 124, 80, 125, 38, 124, 111, 74, 49, 83, 108, 94, 108, 84, 6, 96, 83, 44, 121, 104, 110, 32, 95, 117, 101, 99, 123, 127, 119, 96, 48, 107, 71, 92, 29, 81, 107,
90, 85, 64, 12, 43, 76, 86, 13, 114, 1, 117, 126, 0]
c = ""
i = 0
while (i<56):
a[i]^=b[i]
a[i]^=19
c = c + chr(a[i])
I += 1
print c
得到最后的flag: zsctf{T9is_tOpic_1s_v5ry_int7resting_b6t_others_are_n0t}
第三题 Hello, CTF
0x01.运行程序
输入正确的flag,才会显示正确
0x02.查壳
是32位的程序,并且是Microsoft Visual C++编译,而且没有加壳
0x03.IDA
照旧,依旧先从main开始分析,然后,对main函数进行F5查看伪代码
首先,可以看到先是将字符串复制到v13的位置,
然后,后面对输入进行了判断,输入的字符串不能大于17
接着,将字符串以十六进制输出,然后,再将得到的十六进制字符添加到v10
最后,进行比较,看输入的字符串是否和v10的字符串相等,如果相等,则得到真确的flag
0x04.将字符串转换为十六进制
16进制"437261636b4d654a757374466f7246756e"转换为字符串
#coding:utf-8
a = '437261636b4d654a757374466f7246756e'
c =''
for i in range(0,len(a),2):
s = '0x' + a[i]+a[i+1]
print s
c += chr(int(s,16))
print c
得到了最后的flag是:CrackMeJustForFun
第四题 open-source.c
1.打开源码
打开源码
1 #include
2 #include
3
4 int main(int argc, char *argv[]) {
5 if (argc != 4) {
6 printf("what?\n");
7 exit(1);
8 }
9
10 unsigned int first = atoi(argv[1]);
11 if (first != 0xcafe) {
12 printf("you are wrong, sorry.\n");
13 exit(2);
14 }
15
16 unsigned int second = atoi(argv[2]);
17 if (second % 5 == 3 || second % 17 != 8) {
18 printf("ha, you won't get it!\n");
19 exit(3);
20 }
21
22 if (strcmp("h4cky0u", argv[3])) {
23 printf("so close, dude!\n");
24 exit(4);
25 }
26
27 printf("Brr wrrr grr\n");
28
29 unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;
30
31 printf("Get your key: ");
32 printf("%x\n", hash);
33
34 return 0;
35 }
2. 分析
很明显,第29行计算flag,第32行代码输出十六进制形式。第29行代码就是利用argv[1]~argv[3]的数据进行计算。
2.1 argv[1]
if (first != 0xcafe) {
printf("you are wrong, sorry.\n");
exit(2);
}
不等于0xcafe就退出,那first=0xcafe
2.2 argv[2]
if (second % 5 == 3 || second % 17 != 8) {
printf("ha, you won't get it!\n");
exit(3);
}
满足if条件就退出,我想到第一个不满足的数就是25,second = 25
2.3 argv[3]
if (strcmp("h4cky0u", argv[3])) {
printf("so close, dude!\n");
exit(4);
}
相等strcmp返回0,退出if条件,那argv[3]=“h4cky0u”
3.get flag!
综上,写出解flag代码
#include
#include
int main(int argc, char* argv[]) {
int first = 0xcafe;
int second = 25;
argv[3] = "h4cky0u";
printf("Brr wrrr grr\n");
unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;
printf("Get your key: ");
printf("%x\n", hash);
system("PAUSE");
return 0;
}
运行得到flag:
c0ffee
第五题 simple-unpack
拿到文件先查壳,发现是upx的壳,elf文件
直接放到kali里面构造命令脱壳
Upx -d simple-unpack
脱壳后的文件丢进IDA,明文flag
flag{Upx_1s_n0t_a_d3liv3r_c0mp4ny}
第六题 logmein
然后进入主函数,
经过分析,可以得出:输入的字符要等于 经过处理的v7和v8的异或。v8很明显,但是v7是怎么回事呢,新手没有遇到过,所以上网查看资料,LL是长长整型,v7要转换为16进制然后在转换为字符串,而且字符是小端序,所以把得到的字符翻转然后和v8的每一位进行异或。贴出脚本:
a = 'harambe'
b = ':\"AL_RT^L*.?+6/46'
print(b)
tmp = ''
for i in range(len(b)):
c = ord(a[i % 7]) ^ ord(b[i])
tmp += chr(c)
print(tmp)
运行得flag:
RC3-2016-XORISGUD
第七题 insanity
拿到的同样是elf文件,无壳,直接放到IDA,定位主函数
逻辑简单,没啥好说的,直接跟踪strs,得到flag
9447{This_is_a_flag}
第八题 no-strings-attached
[分析过程]
0x01.查壳和查看程序的详细信息
说明程序是ELF文件,32位
0x02.使用静态分析工具IDA进行分析
然后对main函数使用F5查看伪代码
然后,对每个函数进行跟进,最后发现authenricate(),符合获得flag的函数,对其进行跟进
然后我们发现一个特殊的函数decrypt,根据字面的意思是加密,那么我们可以大概的猜测是一个对dword_8048A90所对应的字符串进行加密,
加密得到的就应该是我们需要的flag,后面的判断应该就是将字符串输出。
这里我们有两种思维方式:
第一种就是跟进decrypt然后分析它的运算逻辑,然后,自己写脚本,得到最后的flag
第二种就涉及逆向的另一种调试方式,及动态调试,这里我就用动态调试了,之前的一直是静态调试
0x03.GDB动态调试
gdb ./no_strings_attached 将文件加载到GDB中
既然是动态调试,那么如果让它一直不停,那我不就相当于运行了嘛,所以,我们就需要下断点,断点就是让程序运行到断点处就停止
之前通过IDA,我们知道关键函数是decrypt,所以我们把断点设置在decrypt处,b在GDB中就是下断点的意思,及在decrypt处下断点
r就是运行的意思,这里运行到了我们之前下的断点处,停止。
我们要的是经过decrypt函数,生成的字符串,所以我们这里就需要运行一步,GDB中用n来表示运行一步
然后我们就需要去查看内存了,去查找最后生成的字符串
通过IDA生成的汇编指令,我们可以看出进过decrypt函数后,生成的字符串保存在EAX寄存器中,所以,我们在GDB就去查看eax寄存器的值
x:就是用来查看内存中数值的,后面的200代表查看多少个,wx代表是以word字节查看看,$eax代表的eax寄存器中的值
在这里我们看到0x00000000,这就证明这个字符串结束了,因为,在C中,代表字符串结尾的就是"\0",那么前面的就是经过decrypt函数生成的falg
那我们就需要将这些转换为字符串的形式
0x04.Write EXP
首先将寄存器中的值提取出来,然后利用Python的decode函数,通过"hex"的方式转化为字符串,然后输出
0x05.运行脚本
得到最后的flag: 9447{you_are_an_international_mystery}
第九题 csaw2013reversing2
[分析过程]
0x01.查壳和查看程序的详细信息
程序是32位的并且没有加壳
0x02.运行程序
发现提示Flag,但是乱码了
载入IDA,静态分析
0x03.IDA
依旧从main函数开始分析,F5查看伪代码
可以看到有个关键函数, 意思是如果在动态调试器中就进入判断运行,如果没有直接弹窗,显示乱码的值
在这里我们看到了一个int 3中断,所以,我们直接OD动态调试,到达中断的位置,应该就能得到正确的flag了
这里我们对比上图的IDA,可以看出mov edx,[ebp+lpMem]对应的汇编指令地址,单步运行F8
执行了mov指令,接下来调用call,F8继续执行,执行完,edx存的就是flag的地址
最后的flag是:flag{reversing_is_not_that_hard!}
还有另外一种做法ida修改:
• sub_40100 为解密函数,必须经过 ,所以修改 jz short loc_4010B9 为 jmp short loc_401096.
• loc_4010B9输 出flag的函数,解密完应跳转到loc_4010B9
•具体修改步骤:
•修改int 3为 NOP.
•修改 jmp short loc_4010EF 为 jmp short loc_4010B9.
•修改 jz short loc_4010B9 为 jmp short loc_401096.
第十题 getit
ida64静态分析,按F5生成pseudocode
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // al
__int64 v5; // [rsp+0h] [rbp-40h]
int i; // [rsp+4h] [rbp-3Ch]
FILE *stream; // [rsp+8h] [rbp-38h]
char filename[8]; // [rsp+10h] [rbp-30h]
unsigned __int64 v9; // [rsp+28h] [rbp-18h]
v9 = __readfsqword(0x28u);
LODWORD(v5) = 0;
while ( (signed int)v5 < strlen(s) )
{
if ( v5 & 1 )
v3 = 1;
else
v3 = -1;
*(&t + (signed int)v5 + 10) = s[(signed int)v5] + v3;
LODWORD(v5) = v5 + 1;
}
strcpy(filename, "/tmp/flag.txt");
stream = fopen(filename, "w");
fprintf(stream, "%s\n", u, v5);
for ( i = 0; i < strlen(&t); ++i )
{
fseek(stream, p[i], 0);
fputc(*(&t + p[i]), stream);
fseek(stream, 0LL, 0);
fprintf(stream, "%s\n", u);
}
fclose(stream);
remove(filename);
return 0;
}
下面是重点代码
LODWORD(v5) = 0;
while ( (signed int)v5 < strlen(s) )
{
if ( v5 & 1 )
v3 = 1;
else
v3 = -1;
*(&t + (signed int)v5 + 10) = s[(signed int)v5] + v3;
LODWORD(v5) = v5 + 1;
}
可以看出将s中的值赋给了t,然后最后是将t写入文件flag.txt中,我们看一下s和t分别是什么
现在我们可以编写python脚本来解出flag
v5 = 0
s = 'c61b68366edeb7bdce3c6820314b7498'
t = ['S','h','a','r','i','f','C','T','F','{','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','}']
v3 = 0
l = len(s)
while(v5 < l):
if( v5 & 1 ):
v3 = 1
else:
v3 = -1
t[10+v5] = chr(ord(s[v5])+v3)
v5 += 1
c = ''
for x in t:
c+=x
print(c)
输出为
SharifCTF{b70c59275fcfa8aebf2d5911223c6589}
第十一题 python-trade.py
是一个pyc文件,用python反编译工具反编译后可以看到代码https://tool.lu/pyc/
import base64
def encode(message):
s = ''
for i in message:
x = ord(i) ^ 32
x = x + 16
s += chr(x)
return base64.b64encode(s)
correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
flag = ''
print 'Input flag:'
flag = raw_input()
if encode(flag) == correct:
print 'correct'
else:
print 'wrong'
编写相应的脚本
#coding:utf-8
import base64
buf = base64.b64decode('XlNkVmtUI1MgXWBZXCFeKY+AaXNt')
flag = ''
for i in buf:
i = ord(i)-16
i ^= 32
flag += chr(i)
print flag
输出flag:
nctf{d3c0mpil1n9_PyC}
第十二题 maze
0x01.查壳和详细信息
可以看到程序是ELF文件,64位
0x02.IDA
对main函数使用F5,查看伪代码
从这里可以看出先是进行判断,如果满足则进入判断,开头必须是以nctf{开头的
然后往下分析
从这里可以看出进行了四个判断,然后,进入四个函数()
这里就涉及逆向的一个有意思的问题那就是迷宫问题:
迷宫问题可以参考:
https://ctf-wiki.github.io/ctf-wiki/reverse/maze/maze/
我们输入的应该是'.','0','o','O',并以此来确定上下左右移动
从上往下以此追踪,可以发现这些函数会跳到lable15的位置,然后,对lable15分析,发现特殊的字符串
然后,猜测可能是一个8*8的迷宫
根据迷宫最后得到的flag:
nctf{o0oo00O000oooo..OO}