• 攻防世界Reverse新手区小结


    退役ACMer(其实ACM也才划水了一个多月)艰难入门CTF逆向工程,第一发学习小结。(啥也不会,只能对着IDA出的代码懵逼,天天坐牢)

    Hello,CTF

    考察sprintf函数

    可以看出输入一个字符串v9,并判断长度小于等于17,再把v9的每个字符赋值给v4,最后通过sprintf函数将v4通过%x(16进制)输出为Buffer字符串,再通过strcat函数将Buffer字符串给v10,最后将v10与v13进行比较。
    程序的意思是字符串转为16进制,那么我们只需要逆向把16进制转为字符串就可以了。

    flag:

    insanity

    shift+F12快速打开string窗口找到flag

    python-trade

    发现附件是一个pyc文件
    于是通过在线反编译网站得到python代码

    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通过encode函数变成和correct相同的字符串。那么对encode函数逆向decode即可。
    分析encode函数,发现ord()函数就是返回一个字符串的ASCII码,chr()函数就是返回一个ASCII码对应的字符串,最后通过base64encode加密。那么decode脚本就好写了。
    先把correct字符串base64decode

    #include<iostream>
    #include<cstring>
    #include<string>
    using namespace std;
    char correct[100]="^SdVkT#S ]`Y!^)ism";
    char flag[40];
    int main()
    {
        for(int i=0;i<strlen(correct);i++)
        {
            char x=(correct[i]-16)^32;
            cout<<x;
        }
    }
    

    不会python,就用C++写了,得到flag。

    re1

    IDA之后F5查看伪代码,发现
    可疑,进入xmmword后,按A键直接转化为字符串得到flag

    game

    IDA后,ctrl+F寻找main函数,得到源代码

    发现了通过游戏的判断条件,双击进入sub函数,发现是对两个数组进行了两次异或运算
    由于地址是连续的,所以v3,v4,以及strcpy的内容其实都属于v2,于是逆向写脚本得到flag。

    open-source
    附件是一段C代码,亲切,直接根据提示修改输出语句即可得到flag。


    改成

    ,并将前面的代码全部删去确保能跑到这一步即可。

    simple-unpack


    拖入Exeinfo查壳发现有壳,需要用upx脱壳。
    将upx.exe和目标文件放入同一目录,打开cmd,输入指令脱壳。

    可以发现脱壳以后文件变大了,再次查壳,发现没了。

    拖入IDA64,反编译后进入flag,直接得到flag

    logmein

    先查壳,发现没壳,拖入IDA,反编译找到main函数(有注释):

    void __fastcall __noreturn main(int a1, char **a2, char **a3)
    {
      size_t v3; // rsi
      int i; // [rsp+3Ch] [rbp-54h]
      char s[36]; // [rsp+40h] [rbp-50h] BYREF
      int v6; // [rsp+64h] [rbp-2Ch]
      __int64 v7; // [rsp+68h] [rbp-28h]
      char v8[28]; // [rsp+70h] [rbp-20h] BYREF
      int v9; // [rsp+8Ch] [rbp-4h]
    
      v9 = 0;
      strcpy(v8, ":"AL_RT^L*.?+6/46");//给v8赋值
      v7 = 0x65626D61726168LL;//16进制数
      v6 = 7;
      printf("Welcome to the RC3 secure password guesser.
    ");
      printf("To continue, you must enter the correct password.
    ");
      printf("Enter your guess: ");
      __isoc99_scanf("%32s", s);//输入s
      v3 = strlen(s);
      if ( v3 < strlen(v8) )//长度不小于v8
        sub_4007C0();
      for ( i = 0; i < strlen(s); ++i )
      {
        if ( i >= strlen(v8) )//长度不大于v8
          sub_4007C0();
        if ( s[i] != (char)(*((_BYTE *)&v7 + i % v6) ^ v8[i]) )//据此直接输出flag
          sub_4007C0();
      }
      sub_4007F0();
    }
    

    于是开始写脚本。

    #include<bits/stdc++.h>
    #define _BYTE unsigned char
    using namespace std;
    
    char a[20]=":"AL_RT^L*.?+6/46";
    long long b=0x65626D61726168LL;
    int c=7;
    char flag[40];
    int main()
    {
        for(int i=0;i<strlen(a);i++)
        {
            flag[i]=(char)(*((_BYTE *)&b + i % c) ^ a[i]);
        }
        cout<<flag;
    }
    

    注:_BYTE为1个字节范围是0-255和unsigned char相同。
    得到flag

    no-strings-attached

    拖入IDA发现 比较可疑

    查看源代码
    发现flag就是通过的decrypt得到的s2,由于之前查出来的
    所以可以用ubuntu上的GDB跑一下。
    先查main的汇编找到目标函数再查目标函数的汇编(gdb是真难用)

    在decrypt处设置断点并执行。

    由于
    说明返回值在eax寄存器中,于是单步调试调用decrpt之后查询eax即可。

    getit

    拖入IDA得到代码,查看字符串内容:

    用快捷键A把完整的t字符串显示出来,发现前缀,猜测应该是通过

    将?填充进去获得完整flag(t[i+10]正好是从第一个问号开始的下标)
    于是开始写脚本。

    #include<bits/stdc++.h>
    using namespace std;
    char s[50]="c61b68366edeb7bdce3c6820314b7498";
    char t[50]="SharifCTF{????????????????????????????????}";
    int main()
    {
        for(int i=0;i<strlen(s);i++)
        {
            int x;
            if(i&1)x=1;
            else x=-1;
            t[i+10]=s[i]+x;
        }
        cout<<t;
    }
    

    得到flag:

    csaw2013reversing2

    先运行,弹出一个窗口,标题为flag,但是是乱码,猜测最后的flag应该也是通过这种形式弹出。扔进Exeinfo PE查壳,没壳,直接扔进IDA反编译出代码:

    int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
    {
      int v3; // ecx
      CHAR *lpMem; // [esp+8h] [ebp-Ch]
      HANDLE hHeap; // [esp+10h] [ebp-4h]
    
      hHeap = HeapCreate(0x40000u, 0, 0);
      lpMem = (CHAR *)HeapAlloc(hHeap, 8u, SourceSize + 1);
      memcpy_s(lpMem, SourceSize, &unk_409B10, SourceSize);
      if ( !sub_40102A() && !IsDebuggerPresent() )
      {
        MessageBoxA(0, lpMem + 1, "Flag", 2u);
        HeapFree(hHeap, 0, lpMem);
        HeapDestroy(hHeap);
        ExitProcess(0);
      }
      __debugbreak();
      sub_401000(v3 + 4, lpMem);
      ExitProcess(0xFFFFFFFF);
    }
    

    注意到有IsDebuggerPresent()函数用来反调试,MessageBoxA是用来弹出一个窗口,IpMem+1即为弹出的内容。
    然后是进入函数sub_401000(),发现很像一个解密函数,那么猜测应该是要执行这个解密函数之后才能改变乱码得到flag。
    考虑动态调试,拖入Ollydbg,先找到IsDebuggerPresent:

    先把它给nop了,然后运行,发现还是弹出了乱码窗口,继续读汇编,发现通过je命令直接跳到了第二个MessageBoxA处,弹出乱码。
    又看到这一行:
    int3是一个断点指令,通过IDA的反编译可以查出是对应__debugbreak()函数,那么下面call的函数很可能是该解密函数,考虑修改je指令跳到该函数前一步:

    又发现后面的jmp指令把两个MessageBoxA都跳过了

    先尝试第一个MessageBoxA,把下面的jmp语句直接nop掉,然后运行:

    弹出空白flag。
    观察两个MessageBoxA的区别:


    考虑到反编译代码中输出的pMem是从1开始的,输出空白flag很可能是因为有占位符'',而下面的MessageBoxA猜测是通过一个寄存器的操作跳过了第一个占位符,那么考虑jmp到下面的MessageBoxA处,修改后的汇编如下:

    运行得到flag:

    maze

    看题目就知道要找地图,直接扔进IDApro反编译:

    __int64 __fastcall main(int a1, char **a2, char **a3)
    {
      __int64 v3; // rbx
      int v4; // eax
      char v5; // bp
      char v6; // al
      const char *v7; // rdi
      unsigned int v9; // [rsp+0h] [rbp-28h] BYREF
      int v10[9]; // [rsp+4h] [rbp-24h] BYREF
    
      v10[0] = 0;
      v9 = 0;
      puts("Input flag:");
      scanf("%s", &s1);
      if ( strlen(&s1) != 24 || strncmp(&s1, "nctf{", 5uLL) || *(&byte_6010BF + 24) != 125 )
      {
    LABEL_22:
        puts("Wrong flag!");
        exit(-1);
      }
      v3 = 5LL;
      if ( strlen(&s1) - 1 > 5 )
      {
        while ( 1 )
        {
          v4 = *(&s1 + v3);
          v5 = 0;
          if ( v4 > 78 )
          {
            if ( (unsigned __int8)v4 == 79 )
            {
              v6 = sub_400650(v10);
              goto LABEL_14;
            }
            if ( (unsigned __int8)v4 == 111 )
            {
              v6 = sub_400660(v10);
              goto LABEL_14;
            }
          }
          else
          {
            if ( (unsigned __int8)v4 == 46 )
            {
              v6 = sub_400670(&v9);
              goto LABEL_14;
            }
            if ( (unsigned __int8)v4 == 48 )
            {
              v6 = sub_400680(&v9);
    LABEL_14:
              v5 = v6;
            }
          }
          if ( !(unsigned __int8)sub_400690(asc_601060, (unsigned int)v10[0], v9) )
            goto LABEL_22;
          if ( ++v3 >= strlen(&s1) - 1 )
          {
            if ( v5 )
              break;
    LABEL_20:
            v7 = "Wrong flag!";
            goto LABEL_21;
          }
        }
      }
      if ( asc_601060[8 * v9 + v10[0]] != 35 )
        goto LABEL_20;
      v7 = "Congratulations!";
    LABEL_21:
      puts(v7);
      return 0LL;
    }
    

    先找到跑图代码:

    对照ASCII码发现:
    O:向左
    o:向右
    0:向下
    .:向上
    然后找到到达终点的代码 盲猜是一个8行的图,且重点为ASCII为35的字符(#)
    进入asc_601060数组,用16进制窗口打开,应该就是地图: 发现地图是8*8的。
    手动画图得到地图:

    ..******
    *...*..*
    ***.*.**
    **..*.**
    *..*#..*
    **.***.*
    **.....*
    ********
    

    跑图得到flag:nctf{o0oo00O000oooo..OO}

    至此,攻防世界新手区完结撒花qwq❀

  • 相关阅读:
    搭建论坛
    修改ecshop页面
    通信失败的原因
    UCenter教程
    discuz论坛 手机登录
    discuz论坛 安装短信插件
    eccshop获取商品详情属性的字段调用值
    php与discuz形成的对接
    php
    php论坛
  • 原文地址:https://www.cnblogs.com/THRANDUil/p/15492622.html
Copyright © 2020-2023  润新知