CTF|reverse方向入门过程

声明:因为懒,所以懒得从头开始做,基本都是看着wp复现的,其中大部分的wp都来源于
https://blog.csdn.net/palmer9/category_9607326.html 话不多说,直接从ctf-wiki上和做题开始
CTF|reverse方向入门过程
文章图片

我们这里可以看到要求
熟悉操作系统(靠学校教的操作系统)
汇编语言(我看了王爽那本然后自己上网学了一点)
加解密(密码学)
具有丰富的多种高级语言编程经验(。。。。。多丰富?)
较强的程序理解和逆向分析能力(太主观了)
带着上述的基础,我们来进行逆向的学习
CTF|reverse方向入门过程
文章图片

1.使用各种工具(对我来说主要是IDA)进行静态分析,收集信息
2.研究程序的保护方法,比如代码混淆,保护壳以及反调试等技术,并设法破除或者绕过保护。
3.反汇编目标软件,能够快速定位到关键代码进行分析
4.结合动态调试,验证自己的初期猜想,在分析的过程中理清程序功能
5.针对程序功能,写出对应脚本,求解出flag
代码混淆
CTF|reverse方向入门过程
文章图片

CTF|reverse方向入门过程
文章图片

CTF|reverse方向入门过程
文章图片

然后直接开始做题 BUU部分
最简单的题目(直接搜索字符串就能看到flag的)
1.[BJDCTF 2nd]guessgame
打开IDA,F12搜索字符串就能看到
CTF|reverse方向入门过程
文章图片

xor
一个64位的文件,直接F5看main函数
CTF|reverse方向入门过程
文章图片

其中重要的语句为
if ( strlen(v6) != 33 ) //判断v6的长度是否为33(从上面的程序中可以看出,v6就是我们的输入)
for ( i = 1; i < 33; ++i ) //进行循环异或,从v6的第二位开始将v6的每一位与前一位异或
v6[i] ^= v6[i - 1];
if ( !strncmp(v6, global, 0x21uLL) ) //比较v6与global段处存放的前33位(也就是0x21)是否相同,是如果相同的话输出success,
大致地看完代码之后我们发现进入global段里面写了什么很重要,我们要做的就是将异或后的v6与global段中的数据进行比对,比对完之后相同就能拿到flag(这里的sucess应该是提示你通过了这道题,并且前面有提示input your flag,也就是要我们输入一串字符(就是v6变量)做为答案。)那么我们只要进入global看到的global的内容就是异或后的v6,也就是异或后的flag
双击进入global看到内容。
CTF|reverse方向入门过程
文章图片

```python str1 = ['f', 0x0A, 'k', 0x0C, 'w', '&', 'O', '.', '@', 0x11, 'x', 0x0D, 'Z', '; ', 'U', 0x11, 'p', 0x19, 'F', 0x1F, 'v', '"', 'M', '#', 'D', 0x0E, 'g', 6, 'h', 0x0F, 'G', '2', 'O']x = 'f'for i in range(1, len(str1)): if (isinstance(str1[i], str)): if (isinstance(str1[i - 1], str)): x += chr(ord(str1[i]) ^ ord(str1[i - 1])) else: x += chr(ord(str1[i]) ^ str1[i - 1]) else: x += chr(str1[i] ^ ord(str1[i - 1]))print(x)

新年快乐(最简单的脱壳)
发现放进IDA里有错误报告,应该是加壳了
先用PE查壳(步骤省略)
直接用万能工具查壳,然后点击脱壳就好了
CTF|reverse方向入门过程
文章图片

CTF|reverse方向入门过程
文章图片

然后得到一个这样的文件,然后就可以放进IDA了,然后看main函数,里面的程序还是相当简单的
CTF|reverse方向入门过程
文章图片

输入一个v5,与v4相等即可拿到flag(v4是HappyNewYear!,在上面可以看到)
v5来源于上面的scanf,也就是我们得到输入
CTF|reverse方向入门过程
文章图片

emmmmm,其实也就是HappyNewYear!做为flag
所以flag是flag{HappyNewYear!}
SimpleRev
点进去看main函数,发现没什么关键的,下面代码的大致意思就是输入的不为d或者D就退出这次循环
和如果输入的为Q或者q就退出,然后看到有一个Decry函数,点进去
CTF|reverse方向入门过程
文章图片

点进Decry函数
CTF|reverse方向入门过程
文章图片

CTF|reverse方向入门过程
文章图片

其中v9注意按R转换成字符串格式
CTF|reverse方向入门过程
文章图片

同时发现有一个join函数,内容如下,大致意思为,
CTF|reverse方向入门过程
文章图片

大致意思为将a1的长度赋值给v2
将a2的长度赋值给v3,然后malloc动态分配空间dest给a1和a2 (大小为a1+a2+1)
然后将a1赋值给dest(strcpy函数),将a2拼接到dest后(strcat函数) (。。。。其实整个函数就是将a1和a2,也就是下面的key3和v9连接起来的意思)
CTF|reverse方向入门过程
文章图片

CTF|reverse方向入门过程
文章图片

unsigned __int64 Decry() { char v1; // [rsp+Fh] [rbp-51h] int v2; // [rsp+10h] [rbp-50h] int v3; // [rsp+14h] [rbp-4Ch] int i; // [rsp+18h] [rbp-48h] int v5; // [rsp+1Ch] [rbp-44h] char src[8]; // [rsp+20h] [rbp-40h] __int64 v7; // [rsp+28h] [rbp-38h] int v8; // [rsp+30h] [rbp-30h] __int64 v9; // [rsp+40h] [rbp-20h] __int64 v10; // [rsp+48h] [rbp-18h] int v11; // [rsp+50h] [rbp-10h] unsigned __int64 v12; // [rsp+58h] [rbp-8h]v12 = __readfsqword(0x28u); *(_QWORD *)src = https://www.it610.com/article/357761762382LL; //(按R变成SLCDN) v7 = 0LL; v8 = 0; v9 = 512969957736LL; v10 = 0LL; v11 = 0; text = (char *)join(key3, &v9); //令text等于key3+v9 //key3="kills"(双击点进key3可知) //v9="hadow"//因为小端序存储 //则 text= killshadow strcpy(key, key1); //将key1赋值给key ,key = "ADSFK"(同样双击可知) strcat(key, src); //将src处的字符拼接到key后 //key = "ADSFKNDCLS"(小端序) v2 = 0; v3 = 0; getchar(); v5 = strlen(key); // v5 = key的长度v5 = 10 for ( i = 0; i < v5; ++i ) { if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )//如果key中存在大写字母,将其变成小写(asc码加32)(这里的v5是10,对10取余等于个位数,而v3最大只到9,所以可以忽略v5) key[i] = key[v3 % v5] + 32; //key = "adsfkndcls" ++v3; } printf("Please input your flag:", src); while ( 1 ) { v1 = getchar(); //接受用户的输入赋值给v1 if ( v1 == 10 )// 如果输入的为换行符,则退出(都可以按R转换看到是\n,以下省略省略) break; if ( v1 == 32 )// 如果输入的为空格,则v2加一 { ++v2; } else { if ( v1 <= 96 || v1 > 122 )// 如果输入的v1不为小写字母 { if ( v1 > 64 && v1 <= 90 )// 如果v1为大写字母 str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97; // 将v1减去39再减去key中下表为[v3++]位置的字母然后加上97之后求除以26的余数再加上97,然后赋值给str2 // str1[v2] = (v1-key[v3]+58)%26 + 97 // 变换后str1[v2]存放小写字母 } else { str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97; //如果v1为小写,则同样处理 } if ( !(v3 % v5) )//如果循环到key的最后一位 putchar(32); //打印一个空格 ++v2; } } if ( !strcmp(text, str2) )// 如果text和str2存储的相同,则成功 puts("Congratulation!\n"); // text = "killshadow" else puts("Try again!\n"); return __readfsqword(0x28u) ^ v12; }

所以我们的目标就是像str2中存入killshadow,其中要满足输入的每个字符减去39再减去key中下标为[v3++]位置的字母然后加上97对26取余然后再加上97,变为killshadow
根据这个要求,我们来编写脚本,首先赋值出一个代表killshadow的变量和一个代表adsfkndcls的变量,然后从大写字母(因为我们的只有我们的大写字母在经过一系列运算之后加上97还能得到的答案是字母的,虽然在字母表中把小写字母写上也没事)中找到一个字母减去39再减去key中下标为v3的字母的asc值再加上97后对26取余再加上97会得到killshadow
(其中97就是a)
key = "adsfkndcls"//赋值给key text = "killshadow"//赋值给text flag = "" _dict = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"//做字母表 v5 = len(text)//赋值给v5 text的长度 for i in range(v5): for v1 in _dict: if ord(text[i]) == (ord(v1) - 39 - ord(key[i % v5]) + 97) % 26 + 97://例: flag += v1 print(flag)

//例:将text中的第一个k取出并且转换成asc值107,然后判断是否等于大写字母表中的一个值减去39和字母a的asc码值(也就是97),然后加上97,再对26取余,再加上97那么就是flag,我们时候诸葛亮的知道第一个正确字母是K,也就是75,减去39再减去97再加上97,得36,对26取余得到10,然后再加上97=107(后续答案同样如此得来)
答案KLDQCUDFZO
//ord,返回其asc码值
[ACTF新生赛2020]easyre
事先脱壳(UPX脱壳,用之前的万能脱壳工具即可)
CTF|reverse方向入门过程
文章图片

前面都是一些定义变量的东西,然后到scanf,输入变量给v19,并且v19如果不等于ACTF{}这些字符,那么return 0
然后定义v16 17 18(?意义不明)
然后解题的重点就在这个循环,那就是判断*(&v4+i)是否等于byte_402000这个数组中下标为的&v16+i-1的数
CTF|reverse方向入门过程
文章图片

我们点进去看到了byte_402000有这些,并且在上面的CTF|reverse方向入门过程
文章图片

循环中我们可以知道flag大概是12位
并且*(&v4+i),意为每次v4的地址加1,也就是v4,v5,v6…这样子,直到v15然后判断是否等于byte-402000
所以如果写脚本的话我们要这样写
先将byte-402000中的值赋值给一个变量,然后将v4到v15的值赋值给另外一个变量,
然后将v4中的值用chr转换成字符,然后检索在byte_402000中的位置,然后把这个位置转换成字符添加到flag变量里
这里要介绍两个函数方法,一个叫做append,作用是将对象添加到末尾
例如
aList = [123, ‘xyz’, ‘zara’, ‘abc’];
aList.append( 2009 );
print "Updated List : ", aList;
结果将会输出Updated List : [123, ‘xyz’, ‘zara’, ‘abc’, 2009],2009被添加到了列表的末尾。
一个叫做find ,作用是检索一个字符串在指定字符串中的位置
例如
str1 = “this is string example…wow!!!”;
str2 = “exam”;
print str1.find(str2);
将会输出15,意为exam这个str2在 str1中的第15个位置开始
其实下面这张图里的这句话这样看,我们会顺眼很多
v4[i]!=byte_402000[flag[i]-1]
CTF|reverse方向入门过程
文章图片

# -*- coding:utf-8 -*- v4 = [42,70,39,34,78,44,34,40,73,63,43,64] model = "}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<; :9876543210/.-,+*)(" + chr(0x27) + r'&%$# !"' pos = [] for i in v4: pos.append(model.find(chr(i))+1)//意为将v4中的数字转换成字符+1然后检索在model中的位置,并且把这个位置+1,然后把这个位置(一个数字)添加到pos后面 s = [chr(x + 1) for x in pos]//然后将pos中的每个数字+1转换成字符,然后一个个组成flag flag = ''.join(s) print ('flag{'+flag+'}')

举个例子大概就是这样,先从v4中取出第一个字符42,然后将转换成字符,也就是*(星号),然后,然后检索*在model中的位置,是83,加1是84,再加1是85,再转换成字符就是85转换成U
*********为什么要两次加1?
答:第一个chr(i)+1是因为位置加1才是下标(下标从0开始)
第二个chr(x+1)是因为原式子中byte_402000[&v16+i]-1,所以要加1补回去
攻防世界部分
insanity(直接搜索字符串就能得到flag)
Mysterious
CTF|reverse方向入门过程
文章图片

CTF|reverse方向入门过程
文章图片

【CTF|reverse方向入门过程】满足v10=123,v12=120,v14=122,v13=121

    推荐阅读