ctf.show | 逆向签到六题

0x01 逆向签到题

有始有终,先来签到,下载后观察题目文件大小为9k,文件不大,notepad++打开,ctrl+F搜索“flag”直接得到。

不嫌麻烦可以用ida(x64)打开,快捷键shift+f12打开字符串窗口即可查看。

完成签到题。

0x02 re2

下载题目并解压,发现两个文件,分别为enflag.txt、勒索病毒.exe。enflag.txt文件不大,使用notepad++打开后发现是乱码。

估计是用可执行文件生成的,防止调试程序时操作该文件,将从而将该加密的文件备份。接下来分析可执行文件,先在沙盒中跑一下,看看有什么功能和UI;发现有这么多字符串,然后按1和2都会退出,不知道干了些啥……但是根据题目描述貌似是一个加密和解密用的软件。

拿着上述的有效信息开始静态分析,查壳后发现无壳。使用ida打开,左侧边栏中寻找start入口,双击查看start代码,双击jmp跟进后发现是call+jmp,推断为VS系列的IDE所开发,而且该可执行文件很可能是Debug版本,找到main函数,并将其标记。

按f5发现无法将其转化为伪代码,原因是调用堆栈不平衡。

使用alt+k将0x401FCC的栈帧改为4。

修改后发现堆栈依然不平衡,如法炮制,将0x401FCE的栈帧修改为0。再次按f5反编译为伪代码查看,发现可以转化;然后将全局数据区的字符串整合,将能识别的函数命名,发现好看多了。

上述部分叙述了如果运行该文件,必须有“flag.txt”可读和“enflag.txt”可写两个条件,那么根据需求在可执行文件的目录下还要建立一个“flag.txt”的文件,此时“flag.txt”为空,而“enflag.txt”为原来的数据”,开始尝试运行可执行文件。

发现可以进行至输入密钥部分,随便输入一些字符发现“enflag.txt”,中的文本被置空,而“flag.txt”并没有变化,说明该程序利用“flag.txt”中的文本加密,得到密文写入“enflag.txt”中,经多次测试证实具有该功能。根据提示,貌似程序中还提供了解密的功能,很可能是密钥输入正确即可根据算法解密。

从输入密钥开始,第一个函数对密钥进行了校验,后面的函数是对明文通过算法的加密过程。那么先进行密钥校验分析,跟进函数查看。

这个函数比较简单,将目标字符串异或0x1F反向得到原文即可,写个程序跑一下。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv, char **envp)
{
    char szBuf[] = { 
        "DH~mqqvqxB^||zll@Jq~jkwpmvez{"
    };

    int nLen = strlen(szBuf);
    printf("Got it:");
    // 输出密钥
    for (int i = 0; i < nLen; i++)
    {
        printf("%c", szBuf[i] ^ 0x1F);
    }

    printf("\r\n");
    system("pause");
    return 0;
}


得到密钥以后再去试试程序,发现可以充值成功了,不过这次把“flag.txt”中写入最初始的密文就可以解密了。>v<)v

0x03 re3

这个题也并不是很难,如果对C的内存结构了解一点的话,很快就能解出。将程序使用ida打开后发现是x64的elf文件。

反汇编代码的sig全白给,一道福利题,直接找main函数,f5反编译成伪代码。

上来就是一大堆变量,这样连续且大小一致的情况,猜测是数组或结构体(不过数组的可能性大一些)。

v5的输入值最后给了数组的最后一个元素v23,所以v23可控,以上是程序的初始化和输入部分,从后面开始为计算和校验部分。

校验和的值最终必须等于0xFFFF。目前有以下思路,将v23先控制为0,将程序定位至遍历数组最后一个元素前,观察校验和,差多少就补多少;所以可以使用gdb动态调试,我偷个懒不开gdb了,直接把代码抄下来改一改用VC++6.0跑一下算了。

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv, char **envp)
{
    int v14 = 0;
    int v15 = 0;
    // 初始化数组
    int nBuf[7] = {
        0x50, 0xFAE3, 0xD7D3F7B, 0xA43499F6, (5 << 12) + 0x10, 0xEF9, 0
    };

    nBuf[6] = 0x1a9f;   // v23可控位置,一开始填写0

    __int64 i64CheckSum = 0;
    for (int i = 0; i <= 6; ++i )
    {
        for ( i64CheckSum += (unsigned int)*(nBuf + (signed int)i);
            i64CheckSum > 0xFFFF;
            i64CheckSum = v15 + (unsigned int)(unsigned __int16)i64CheckSum)
        {
            v14 = (unsigned __int16)i64CheckSum;
            v15 = (int)(i64CheckSum >> 16);
        }
    }
    if ( i64CheckSum == 0xFFFF )
        puts("OK");
    else
        puts("Error");
    system("pause");
    return 0;
}

最后题目要求说最小值且是4位,其实十进制也是4位,不过这里答案是十六进制的,记得拼接一下“flag{}”,ahhh。

0x04 武穆遗书

总算看到一道中规中矩的技术题,最近这逆向的题目越来越离谱了,不跟你对抗技术,就跟你玩数学,技术不够数学来凑,做题都做佛了;另外ctfshow站长同志该更新题目描述了啊……

32位pe+UPX壳,上x32dbg开始手脱。

加载好程序后,将程序定位至用户代码,向下f8一直走,遇到循环直接f4到后面,直到找到长跳转。

找到该处长跳转后,再单步一次即为解压后的代码,也就是程序的真实入口点。

使用自带的scylla工具进行dump,设置当前地址为eip后,进行dump。

然后将dump的文件进行修复导入表,双击文件不能正常运行,但可以用x32dbg调试运行。

依然定位用户代码,然后找到main函数,然后步入。

上面四个call和下面还有一个call均用来做反调试,不信可以单步过去看,反复检查调试的标志位,可以一个个跳过去,但其中一个是异常处理,貌似无法跳过,改成nop可以解决。我就比较暴力了,把上面的几个call全填写为nop,然后最后一个call直接修改ZF标志位强行跳转。

显示提示信息后,随便输入点东西,然后通过修改ZF标志位跳过后方的判断直接看比较部分,由于使用了strcmp导致源码泄露,直接能看到缓冲区中的flag。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇