攻防世界-REVERSE新手练习 解题思路
逆向工具下载
题一.open-source
原题:
难度系数: ★★★
题目来源: HackYou CTF
题目描述: 菜鸡学逆向学得头皮发麻,终于它拿到了一段源代码
题目场景: 暂无
题目附件: 附件1
原题链接
解题思路
打开源码就可以看见主函数一运行就是一个判断。
if (argc != 4) {
printf("what?\n");
exit(1);
}
但是可以看到,argc这个变量只在第一次判断的时候被调用了,所以直接把这个判断去掉也能顺利运行。
继续往下看第二个判断:
unsigned int first = atoi(argv[1]);
if (first != 0xcafe)
{
printf("you are wrong, sorry.\n");
exit(2);
}
如果first不等于0xcafe就退出,直接倒着推first是0xcafe就继续执行。所以first的值是0xcafe
继续往下看
unsigned int second = atoi(argv[2]);
if (second % 5 == 3 || second % 17 != 8) {
printf("ha, you won't get it!\n");
exit(3);
}
还是一样,根据他的判断来倒着推出式子。
for(int second{0};true;++second)
if((second % 5 != 3) && (second % 17 == 8))
{
std::cout << second;
break;
}
执行完之后得到我们的second为25
接着看最后一个判断
if (strcmp("h4cky0u", argv[3])) {
printf("so close, dude!\n");
exit(4);
}
strcmp函数字符串比较,用于比较两个字符串并根据比较结果返回整数。基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数。
所以得到结果argv[3]就是"h4cky0u"。
最后我们把我们得到的值套进公式里面进行计算。
#include <iostream>
int main()
{
unsigned int first = 0xcafe;
unsigned int second = 25;
char* argv[0xff];
argv[3] = "h4cky0u";
unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;
printf("%x\n", hash);
}
flag:c0ffee
题二.simple-unpack
原题:
难度系数: ★★★
题目来源: 暂无
题目描述: 菜鸡拿到了一个被加壳的二进制文件
题目场景: 暂无
题目附件:附件1
原题地址
解题思路
下载好后附件拖入Exeinfo_PE查看,得知这是一个Linux下的二进制文件。
注:Windows下的文件是PE文件,Linux/Unix下的文件是ELF文件。
它不仅是一个ELF文件他还被PUX进行了加壳,那我们现在就用UPX进行脱壳,进入软件路径后使用脱壳指令给附件解密。
upx -d X
这个X就是我们需要脱壳的程序
前去GitHub下载UPX(UPX自带解密):UPX下载地址
OK解密之后就直接拖入IDA进行查看.flag直接出现在我们面前
flag:flag
题三.logmein
原题:
难度系数: ★★★
题目来源: RC3 CTF 2016
题目描述: 菜鸡开始接触一些基本的算法逆向了
题目场景: 暂无
题目附件:附件1
原题地址
解题思路
还是和上一题一样。下载附件然后拖入Exeinfo_PE查看,得知这又是一个Linux下的二进制文件。
但是它这次没有加密,还算是简单。
直接拖入IDA进行查看一下。
稍微翻译一下英文就看到了“You entered the correct password!Great job!”这一句。我们直接点进去找到关键函数。
找到函数后双击进去,按下F5查看伪函数。
分析完之后按下X键,查看交叉引用。看看都有谁使用了这个函数,然后直接双击跳到使用的地方。
OK跳过来之后发现这里就是主函数,算法部分貌似也是写在了主函数里面。
大概看了下他主函数的内容。
最主要的判断就是最后一个v7 v6 v8三个变量的异或。
if(s[i]!= (char)(*((_BYTE *)&v7 + i % v6) ^ v8[i]) )
只要把三个变量用循环异或一下得到的值就是我们想要的flag
#include <iostream>
#include <string.h>
#define BYTE char
int main()
{
std::string key1 = ":\"AL_RT^L*.?+6/46";
__int64 key2 = 28537194573619560;
int key3 = 7;
char Flag[0xff]{};
for (size_t i = 0; i < key1.length(); i++)
{
Flag[i] = (char)(*((BYTE*)&key2 + i % key3) ^ key1[i]);
}
std::cout << Flag;
}
flag:RC3-2016-XORISGUD
题四.insanity
原题
难度系数: ★★★
题目来源: 9447 CTF 2014
题目描述: 菜鸡觉得前面的题目太难了,来个简单的缓一下
题目场景: 暂无
题目附件: 附件1
原题地址
解题思路
还是一样下载附件直接拖入查壳软件查壳。发现是一个32位的ELF文件。
那没办法OD不能用只能拖入IDA查看了。
拖入IDA发现直接就有主函数的入口。直接双击之后F5查看伪函数。
然后发现这个函数是输出的这个字符串。猜测和flag有关联,双击进去之后发现 flag就直接出现在我们的面前了。
一开始还以为只有{}内的字符是flag哪知道9447也是flag
flag:9447
题五.python-trade
原题
难度系数: ★★★
题目来源: NJUPT CTF 2017
题目描述: 菜鸡和菜猫进行了一场Py交易
题目场景: 暂无
题目附件: 附件1
原题地址
解题思路
附件下载下来之后发现是一个.pyc的的文件
注:pyc文件是py文件编译后生成的字节码文件。
我们直接利用Py在线反编译工具进行代码还原。
以下就是反编译后代码
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'
看了下大概流程就是输入一段字符然后进行加密,看加密过后是不是等于内置的字符串。
那直接拿它内置的字符串进行解密就可以得到他的flag了。
再看一下他加密函数的流程。先给字符串拆分成数组进行循环ord函数是字符转ascll码,然后和32异或。接着加上16,然后把每个元素进行拼接。最后字符串进行base64加密。
那首先我们把内置码进行base64解密。
然后给他拆分成数组然后-16之后^32 然后给它合并成字符串。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string Nei_Code{ "^SdVkT#S ]`Y\!^)ism" };
string Flag{};
for ( int i = 0; i < Nei_Code.length(); i++)
{
Flag += (char)(Nei_Code[i] - 16) ^ 32;
}
cout << Flag;
}
运行之后得到我们的Flag
flag:nctf
题六.game
原题
难度系数: ★★★★
题目来源: ZSCTF
题目描述: 菜鸡最近迷上了玩游戏,但它总是赢不了,你可以帮他获胜吗
题目场景: 暂无
题目附件: 附件1
原题地址
解题思路
下载附件之后直接进行查壳。终于是来了一个可执行文件了。
无壳,直接拖OD看看吧。
运行之后发现题目邀请是让我把所有灯全部点亮,但是在点亮这个灯的时候会把旁边两个灯的状态取反。
先拿OD看看字符串之类的。
看到了貌似很关键的地方。双击进去看看。
进来之后发现这个字符串下面有很长的一段十六进制数。那这肯定就是要找到flag了,但是它太长了一个一个转换过来太麻烦。想个办法让他执行起来,来到函数头,发现是一个很长的跳转。
这就很明显了,它这个程序的函数都是使用长跳转进的函数。我们只要找到它会执行的一个函数把他的jmp的地址改成这个函数的地址就行了。
有点像我们要找的地方,点进去看看之后确实是一个函数,然后去函数头部给移动到它跳转的地址。给他改成jmp 00E2E940
OK 随便输入一个数看看效果。flag直接出现了。
flag:zsctf
题七.Hello, CTF
原题
难度系数: ★★★★
题目来源: Pediy CTF 2018
题目描述: 菜鸡发现Flag似乎并不一定是明文比较的
题目场景: 暂无
题目附件: 附件1
原题地址
解题思路
还是一样下载完附件之后先查壳。
无壳,可执行文件先拖OD查字符串看看。能够很明显看见一个成功,一个错误,直接点进去看看。
然后发现并没有什么可以得到它flag的地方,现在再拖IDA看看的判断算法。
注:因为弄算法IDA比OD方便很多。
拖入IDA之后发现一段很奇怪的数据,看伪函数如何给他进行解密。
从图中可以看出来,它把这段数据复制给了v13这个变量
然后,又对输入进行了判断,输入的字符串不能大于0x11
接着,将字符串以十六进制输出
然后,再将得到的十六进制字符添加到v10
最后,进行比较,看输入的字符串是否和v10的字符串相等,如果相等,则得到真确的flag。
那直接把内置的16进制码转成字符就得到想要的flag了
flag:CrackMeJustForFun
题八.getit
原题
难度系数: ★★★★
题目来源: SharifCTF 2016
题目描述: 菜鸡发现这个程序偷偷摸摸在自己的机器上搞事情,它决定一探究竟
题目场景: 暂无
题目附件: 附件1
原题地址
解题思路
下载完附件之后,进行查壳。发现是一个Linux下的64位文件
拖进IDA,看看为伪代码。
它首先是判断v5的大小是不是小于s的长度。
然后再进行一个运算将flag写入到“/tmp/flag.txt”文件里面。所以他的算法就是要的flag。然后我们直接写一遍运行一下就得到了flag
#include <iostream>
#include <string>
#include <windows.h>
using namespace std;
int main()
{
string s = "c61b68366edeb7bdce3c6820314b7498";
DWORD v5 = 0;
char v3;
string flag{};
while ( v5<s.length())
{
if (v5&1) v3 = 1;
else v3 = -1;
flag += (char)((int)(s[v5]) + v3);
v5++;
}
cout << flag;
}
但是它前面还有一个变量t
我们还得看看t是什么东西。
大概已经猜想到他的flag是什么了。
flag:SharifCTF
题九.re1
原题
难度系数: ★★★★★
题目来源: DUTCTF
题目描述: 菜鸡开始学习逆向工程,首先是最简单的题目
题目场景: 暂无
题目附件: 附件1
原题地址
解题思路
下载完附件之后,进行查壳。发现是一个无壳的可执行文件
直接上OD运行
入口的,但是进去之后发现要逆算法,还是进IDA看。
拖入IDA找到主函数按F5
我们双击进去之后,这一看就是一个字符串。直接按R键转换成字符。
把两个字符合并之后就是要找的一个flag
注:这里要跟大家普及一个知识了,及大端与小端
假设一个十六进制数0x12345678
大端的存储方式是:12,34,56,78,然后读取的时候也是从前往后读
小端的存储方式是:78,56,34,12,然后读取的时候是从后往前读取
所以,最后的flag应该是:DUTCTF
题十.no-strings-attached
原题
难度系数: ★★★★★
题目来源: 9447 CTF 2014
题目描述: 菜鸡听说有的程序运行就能拿Flag?
题目场景: 暂无
题目附件: 附件1
原题地址
解题思路
下载完附件之后,进行查壳。发现是一个无壳Linux下的32位文件
直接进IDA,找到主函数,按F5查看它的一个伪代码
这主函数里面就这么几个函数,分别进入函数分析,发现banner是输出程序提示信息的,prompt_authentication是程
序提示我们输入authentication details。
而authenticate则是通过比较输入与decrypt解密后得到的字符串是否相等判断是否通过验证。这说明flag就是decrypt(&s, &byte_8048A90)得到的字符串。
进入这个函数分析一下逻辑
分析它的逻辑后发现,它先把参数s复制给dest,然后把dest的每个值减去a2的值
然后返回加密后的dest,再在上级调用函数里与输入的字符串进行比较,相同则提示Success!不相同则提示Access denied!
然后我们把两个参数的值取出来。双击参数,选中按shift+e。
注意:两个参数的类型都是 wchar_t 类型(长度 16 位或 32 位,本机 32 位,4 字节) 由于有大量的 0,所以不能用 char 类型的数组,否则读到第三位直接结束。此外,删除后面 4 个字节的 0,因为字符串的结尾默认加 0。
然后我们把代码写出来
#include <iostream>
using namespace std;
int main()
{
wchar_t a[] =
{
0x1401,0x1402,0x1403,0x1404,0x1405
};
wchar_t dest[] =
{
0x143a,0x1436,0x1437,0x143b,0x1480,
0x147a,0x1471,0x1478,0x1463,0x1466,
0x1473,0x1467,0x1462,0x1465,0x1473,
0x1460,0x146b,0x1471,0x1478,0x146a,
0x1473,0x1470,0x1464,0x1478,0x146e,
0x1470,0x1470,0x1464,0x1470,0x1464,
0x146e,0x147b,0x1476,0x1478,0x146a,
0x1473,0x147b,0x1480,0x0
};
int v4{};
for (int i = 0, j = 0; i < wcslen(dest); i++)
{
dest[i] -= a[j++];
j = j < 5 ? j : 0;
}
wprintf(dest);
}
然后计算出想要的flag
flag:9447