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('\n')
    list11=line.split('=')
    list1.append(list11[1])
for line in file2:
    line=line.strip('\n')
    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('\n')
file1.close()
file2.close()
file3.close()
list4=[]
file4=open("3.txt",'r')
for line in file4:
    line=line.strip('\n')
    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]='\0';
    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\n", 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\n", 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}

posted @ 2020-01-24 00:23  Theffth  阅读(469)  评论(0编辑  收藏  举报