• adworld-re新手练习区


    RE

    re1

    下载得到exe文件,运行:

     

    使用exeinfo分析,发现是32位无壳的exe,拖入IDA,f5反编译:

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      int v3; // eax
      __int128 v5; // [esp+0h] [ebp-44h]
      __int64 v6; // [esp+10h] [ebp-34h]
      int v7; // [esp+18h] [ebp-2Ch]
      __int16 v8; // [esp+1Ch] [ebp-28h]
      char v9; // [esp+20h] [ebp-24h]
    ​
      _mm_storeu_si128((__m128i *)&v5, _mm_loadu_si128((const __m128i *)&xmmword_413E34));
      v7 = 0;
      v6 = qword_413E44;
      v8 = 0;
      printf(&byte_413E4C);
      printf(&byte_413E60);
      printf(&byte_413E80);
      scanf("%s", &v9);
      v3 = strcmp((const char *)&v5, &v9);
      if ( v3 )
        v3 = -(v3 < 0) | 1;
      if ( v3 )
        printf(aFlag);
      else
        printf((const char *)&unk_413E90);
      system("pause");
      return 0;
    }

    追踪查看了unk_413E90中存放的内容是:flagget 所以v3运行结果应该是0,也即v5和v9中存放的东西相同;所以去找v5中的内容,发现:

      _mm_storeu_si128((__m128i *)&v5, _mm_loadu_si128((const __m128i *)&xmmword_413E34));

    猜测意思是将xmmword_413E34的内容赋值给v5,所以继续看xmmword_413E34的内容,得到flag:DUTCTF{We1c0met0DUTCTF}

    注:

    void  _mm_storeu_si128(_m128i *p,_m128i a)

    SSE指令,将_m128i变量a的值存储到p所指定的变量中

    _m128i _mm_loadu_si128(_m128i *p)

    SSE指令,返回寄存器中的值

    game

    打开题目,玩一会2333,好吧,玩是玩不出flag的,放入exeinfo,发现32位,拖入IDA,找到main主函数,f5反编译,发现全部变成1后,会执行sub_457AB4( )函数:

     

    跟进它:

    来到sub_45E940( )函数,发现关键部分:

    写脚本跑一下:

    list1=[]
    list2=[]
    file1 = open("1.txt",'r')
    file2 = open("2.txt",'r')
    file3 = open("3.txt",'w')
    for line in file1:
        line=line.strip('
    ')
        list11=line.split('=')
        list1.append(list11[1])
    for line in file2:
        line=line.strip('
    ')
        list12=line.split('=')
        list2.append(list12[1])
    ​
    list3=[]
    for i in range(57):
        list3.append(int(list1[i])^int(list2[i]))
        file3.write(str(list3[i]))
        file3.write('
    ')
    file1.close()
    file2.close()
    file3.close()
    list4=[]
    file4=open("3.txt",'r')
    for line in file4:
        line=line.strip('
    ')
        list4.append(line)
    for i in range(57):
        list4[i]=chr(int(list4[i])^0x13)
        
    print ''.join(list4)

    注:如果IDA不能正确的获得自定义函数的名字,那么IDA会用sub__加上自定义函数的起始地址来定义函数的名字

    Hello,CTF

    打开题目,IDA->f5,发现关键代码:

    要求输入的字符串与v13相同且长度不超过17,题目提示不一定是明文比较,所以将v13十六进制转换成字符串:CrackMeJustForFun,也即flag

    注:

    int sprintf(char *string, char *format [,argument,...]);

    说明:把格式化(format与其后数量不定的参数)的数据写入某个字符串缓冲区(string),有缓冲区溢出的可能,不安全

    open-source

    下载得到一个C源码,

    将上面的参数值代入计算,得到flag.

    Simple-unpack

    exeinfo分析下载的文件:

    Linux upx -d 脱壳,载入IDA发现flag.

    logmein

    exeinfo显示下载文件为64位ELF文件,拖入IDA,f5反编译:

    要求flag长度应与v8相同,且每一位都是由v7和v8的对应字符异或得到,所以编写EXP:

    v7="harambe"  #重点!
    str=":"AL_RT^L*.?+6/46"
    flag=''
    for i in range(len(str)):
        flag += chr(ord(str[i])^ord(v7[i%7]))
        
    print flag

    或者C++脚本:

    #include <iostream>
    #include <cstring>
    typedef unsigned char BYTE;
    using namespace std;
    /* run this program using the console pauser or add your own getch, system("pause") or input loop */int main(int argc, char** argv) {
        long long int v7=28537194573619560;
        char v8[18] =":"AL_RT^L*.?+6/46";
        char s[18];//定义字符数组变量,char a[字符串长度+1];
        int i;
        for(i=0;i<strlen(v8);i++)
        {
            s[i]=(char)(*((BYTE *)&v7+i%7)^v8[i]);
        }
        s[i]='';
        cout<<s;
        return 0;
    }

    跑出flag.

    注:

    1.在计算机中数据全部以二进制形式储存,所以v7是int型还是字符串型本质上是相同的,但是在该程序中,计算机以小端字节序储存数据,所以v7变成字符串时应该从后向前读取。

    2.v8是":"AL_RT^L*.?+6/46",该字符串长度应为17,其中包含转移字符"

    3.IDA宏定义:

    typedef unsigned char uint8;  
    ​
    #define _BYTE  uint8   //所以说_BYTE代表了四个字节
    insanity

    ida->f5查看伪码:

    大概意思应该是不断地随机(以程序运行时间为种子,取随机数)取strs这个字符串中的字符,跟进strs,得到flag:9447{This_is_a_flag}

    no-strings-attached

    拿到下载文件放进exeinfo分析一下:

    为32位ELF文件,拖进IDA分析,找啊找,找到decrypt函数!

    wchar_t *__cdecl decrypt(wchar_t *s, wchar_t *a2)
    {
      size_t v2; // eax
      signed int v4; // [esp+1Ch] [ebp-1Ch]
      signed int i; // [esp+20h] [ebp-18h]
      signed int v6; // [esp+24h] [ebp-14h]
      signed int v7; // [esp+28h] [ebp-10h]
      wchar_t *dest; // [esp+2Ch] [ebp-Ch]
    ​
      v6 = wcslen(s);
      v7 = wcslen(a2);
      v2 = wcslen(s);
      dest = (wchar_t *)malloc(v2 + 1);
      wcscpy(dest, s);
      while ( v4 < v6 )
      {
        for ( i = 0; i < v7 && v4 < v6; ++i )                    // v6是s的长度  v7是a2的长度  dest是s
          dest[v4++] -= a2[i];
      }
      return dest;
    }

    意思是s的每一位循环减去a2的每一位,最后存入dest。

    找到调用位置----

    void authenticate()
    {
      int ws[8192]; // [esp+1Ch] [ebp-800Ch]
      wchar_t *s2; // [esp+801Ch] [ebp-Ch]
    ​
      s2 = decrypt(&s, &dword_8048A90);
      if ( fgetws(ws, 0x2000, stdin) )
      {
        ws[wcslen(ws) - 1] = 0;
        if ( !wcscmp(ws, s2) )
          wprintf((int)&unk_8048B44);
        else
          wprintf((int)&unk_8048BA4);
      }
      free(s2);
    }

    可以看出flag应该就是加密后的密文,下面有两种方法解决——

    方法一:静态分析,编写exp

    #include <iostream>
    using namespace std;
    ​
    /* run this program using the console pauser or add your own getch, system("pause") or input loop */int main(int argc, char** argv) {
        int i=1,j=0;
        int a[]={0x3A,0x36,0x37,0x3B,0x80,0x7A,0x71,0x78,0x63,0x66,0x73,0x67,0x62,0x65,0x73,0x60,0x6B,0x71,0x78,0x6A,0x73,0x70,0x64,0x78,0x6E,0x70,0x70,0x64,0x70,0x64,0x6E,0x7B,0x76,0x78,0x6A,0x73,0x7B,0x80,0};
        char t;
        while(a[j]!=0)
        {
            t=a[j++]-(i++);
            cout<<t;
            if(i==6)
            {
                i=1;
            }
        }   return 0;
    }

    跑出flag: 9447{you_are_an_international_mystery}

    方法二:linux下gdb动态分析(来自平台wp)

    gdb 文件路径

    先在dectypt函数处下断点---- b decrypt

    然后运行程序至断点处---- r

    单步执行---- n

    在IDA中可以看到

    加密后的结果存入了寄存器eax中,

    所以最后查看运行结果---- x/50wx $eax【注:wx为以word即两个字节32bit形式查看】

    16进制转ASCII同样得到flag。

    csaw2013reversing2

    exeinfo发现32bit 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, MaxCount + 1);
      memcpy_s(lpMem, MaxCount, &unk_409B10, MaxCount);
      if ( sub_40102A() || IsDebuggerPresent() )
      {
        __debugbreak();
        sub_401000(v3 + 4, lpMem);
        ExitProcess(0xFFFFFFFF);
      }
      MessageBoxA(0, lpMem + 1, "Flag", 2u);
      HeapFree(hHeap, 0, lpMem);
      HeapDestroy(hHeap);
      ExitProcess(0);
    }

    关键是if( )函数,其中sub_401000应该为得到flag的函数,

    unsigned int __fastcall sub_401000(int a1, int a2)
    {
      int v2; // esi
      unsigned int v3; // eax
      unsigned int v4; // ecx
      unsigned int result; // eax
    ​
      v2 = dword_409B38;
      v3 = a2 + 1 + strlen((const char *)(a2 + 1)) + 1;
      v4 = 0;
      result = ((v3 - (a2 + 2)) >> 2) + 1;
      if ( result )
      {
        do
          *(_DWORD *)(a2 + 4 * v4++) ^= v2;
        while ( v4 < result );
      }
      return result;
    }

    但是跟进sub_40102A发现永远return 0;而另一个条件则在调试状态下成立,因此暂时有三种思路可以解决这个问题:

    (一)IDA静态分析

    分析sub_401000( )函数,自己编写函数实现的脚本跑出flag:

    #include <iostream>
    using namespace std;
    typedef unsigned char BYTE;
    /* run this program using the console pauser or add your own getch, system("pause") or input loop */int main(int argc, char** argv) {
        int v2[] ={0xBB,0xAA,0xCC,0xDD};
        char result[]={0xBB,0xCC,0xA0,0xBC,0xDC,0xD1,0xBE,0xB8,0xCD,0xCF,0xBE,0xAE,0xD2,0xC4,0xAB,0x82,0xD2,0xD9,0x93,0xB3,0xD4,0xDE,0x93,0xA9,0xD3,0xCB,0xB8,0x82,0xD3,0xCB,0xBE,0xB9,0x9A,0xD7,0xCC,0xDD};
        int i;
        for(i=0;i<0x24;i++)
        {
            result[i]^=v2[i%4];
            cout<<result[i];
        }
        return 0;
    }

    注:

    1.在main()中可以看到由memcpy_s( )函数对lpmem空间初始化,也即result[]初值

    2.分析memcpy_s( )函数可以看到每个单元为8bit,也即两个十六进制位,所以实现异或时,v2也应8bit为一个单元进行异或,即v2[]初值

    (二)OllyDBG动态调试

    在调试状态下,执行if( )函数:

    首先int 3为断点指令——

    所以调试到int 3停止,在OD中f8单步执行,

    随后edx寄存器中存储函数执行结果的地址,查看得到flag:

    (三)IDA改变函数执行逻辑

    修改汇编语言指令,再次运行,得到flag:

    getit

    exeinfo分析知为64bit ELF文件,IDA反汇编分析:

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      char v3; // al
      __int64 v5; // [rsp+0h] [rbp-40h]
      int i; // [rsp+4h] [rbp-3Ch]
      FILE *stream; // [rsp+8h] [rbp-38h]
      char filename[8]; // [rsp+10h] [rbp-30h]
      unsigned __int64 v9; // [rsp+28h] [rbp-18h]
    ​
      v9 = __readfsqword(0x28u);
      LODWORD(v5) = 0;
      while ( (signed int)v5 < strlen(s) )
      {
        if ( v5 & 1 )
          v3 = 1;
        else
          v3 = -1;
        *(&t + (signed int)v5 + 10) = s[(signed int)v5] + v3;
        LODWORD(v5) = v5 + 1;
      }             // flag实现过程
      strcpy(filename, "/tmp/flag.txt");
      stream = fopen(filename, "w");
      fprintf(stream, "%s
    ", u, v5);
      for ( i = 0; i < strlen(&t); ++i )
      {
        fseek(stream, p[i], 0);
        fputc(*(&t + p[i]), stream);
        fseek(stream, 0LL, 0);
        fprintf(stream, "%s
    ", u);
      }            // p[]中为0x01-0x47,即将刚刚得到的flag写入文件
      fclose(stream);
      remove(filename);
      return 0;
    }

    大致是运行程序,会在当前目录下创建文件并写入内容,最后将文件删除的过程,所以写脚本实现flag输出:

    #include <iostream>
    #include <string>
    using namespace std;
    /* run this program using the console pauser or add your own getch, system("pause") or input loop */int main(int argc, char** argv) {
        string t="SharifCTF{";
        string s="c61b68366edeb7bdce3c6820314b7498";
        int v5=0,v3,i;
        for(v5=0;v5<s.length();v5++)
        {
            if(v5&1)
            {
                v3=1;
            }
            else 
            {
                v3=-1;
            }
            t+=(s[v5]+v3);
        }
        t+="}"; 
        cout<<t;
        return 0;
    }
    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:

    import base64
    ​
    def decode(message):
        s = base64.b64decode(message)
        result=''
        for i in s:
            i=ord(i)-16
            i=i^32
            result+=chr(i)
        return result
    ​
    ​
    correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
    flag = decode(correct)
    print flag
    maze

    迷宫问题:

    IDA反汇编得到的伪码如下:

    __int64 __fastcall main(__int64 a1, char **a2, char **a3)
    {
      signed __int64 v3; // rbx
      signed int v4; // eax
      bool v5; // bp
      bool v6; // al
      const char *v7; // rdi
      __int64 v9; // [rsp+0h] [rbp-28h]
    ​
      v9 = 0LL;
      puts("Input flag:");
      scanf("%s", &s1, 0LL);
      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 > 'N' )
          {
            v4 = (unsigned __int8)v4;
            if ( (unsigned __int8)v4 == 'O' )       // 向左走
            {
              v6 = sub_400650((_DWORD *)&v9 + 1);
              goto LABEL_14;
            }
            if ( v4 == 'o' )                        // 向右走
            {
              v6 = sub_400660((int *)&v9 + 1);
              goto LABEL_14;
            }
          }
          else
          {
            v4 = (unsigned __int8)v4;
            if ( (unsigned __int8)v4 == '.' )       // 向上走
            {
              v6 = sub_400670(&v9);
              goto LABEL_14;
            }
            if ( v4 == '0' )                        // 向下走
            {
              v6 = sub_400680((int *)&v9);
    LABEL_14:
              v5 = v6;
              goto LABEL_15;
            }
          }
    LABEL_15:
          if ( !(unsigned __int8)sub_400690((__int64)asc_601060, SHIDWORD(v9), v9) )
            goto LABEL_22;
          if ( ++v3 >= strlen(&s1) - 1 )
          {
            if ( v5 )
              break;
    LABEL_20:
            v7 = "Wrong flag!";
            goto LABEL_21;
          }
        }
      }
      if ( asc_601060[8 * (signed int)v9 + SHIDWORD(v9)] != 35 )
        goto LABEL_20;
      v7 = "Congratulations!";
    LABEL_21:
      puts(v7);
      return 0LL;
    }

    其中的LABEL只是吓人的,其实代码本身逻辑还是很清楚的,其中限制我一开始没看懂的关键有几点:

    (1)

    v6 = sub_400650((_DWORD *)&v9 + 1);

    typedef unsigned long DWORD;

    DWORD是指两个word、四个字节、32bit。因此,这里的函数参数是变量v9的后一个32bit存储单元的指针。

    (2)

    (unsigned __int8)sub_400690((__int64)asc_601060, SHIDWORD(v9), v9)

    其中,

    #define SHIDWORD(x)  (*((int32*)&(x)+1))

    也就是指:变量v9的下一个指向32bit内存单元的指针所指的内容,也就是上面那个函数的参数

    (3)

    __int64 __fastcall sub_400690(__int64 a1, int a2, int a3)
    {
      __int64 result; // rax
    ​
      result = *(unsigned __int8 *)(a1 + a2 + 8LL * a3);
      LOBYTE(result) = (_DWORD)result == 32 || (_DWORD)result == 35;
      return result;
    }

    最关键的是result那一行,分析后可以转变成:a2为列数、a3为行数,也就是第二个参数是列数,于是可以倒推出上面四个函数中符号代表的方向。

    =>于是,条件是:

    flag为24位字符串,格式为nctf{xxxxxxx}

    'O','o','.','0'分别代表向左、向右、向上、向下

    迷宫为:

    00******
    *000*00*
    ***0*0**
    **00*0**
    *00*#00*
    **0***0*
    **00000*
    ********

    因此,手动得到flag:nctf{o0oo00O000oooo..OO}

  • 相关阅读:
    函数、包和错误处理
    程序流程控制
    poj 2515 Birthday Cake
    poj 2094 多项式求和。
    hdu 3625 第一类striling 数
    hdu 4372 第一类stirling数的应用/。。。好题
    poj 1845 Sumdiv
    hdu 3641 Treasure Hunting 强大的二分
    poj 3335 /poj 3130/ poj 1474 半平面交 判断核是否存在 / poj1279 半平面交 求核的面积
    hdu 2841 Visible Trees
  • 原文地址:https://www.cnblogs.com/Theffth-blog/p/12231751.html
Copyright © 2020-2023  润新知