攻防世界-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

posted @ 2020-07-13 16:26  LuckyMX  阅读(2010)  评论(0编辑  收藏  举报