adworld-re novice practice area

RE

re1

Downloaded exe file, run:

 

Use exeinfo analysis and found to be 32-bit free shell exe, drag IDA, f5 decompile:

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;
}

Check the contents of the track is stored in unk_413E90: flagget so v3 operating results should be zero, that is, v5 and v9 stored in the same things; so look for content in v5, we found:

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

Means assigning guess xmmword_413E34 content to V5, it continues to see the contents xmmword_413E34 obtain flag: DUTCTF {We1c0met0DUTCTF}

Note:

void  _mm_storeu_si128(_m128i *p,_m128i a)

SSE instructions, the value of a variable _m128i p stored in the variable specified in

_m128i _mm_loadu_si128 (_m128i * p)

SSE instructions, return value registers

game

Open title, will play a 2333, well, play to play not flag, and put exeinfo, found 32, dragged into the IDA, to find the main primary function, f5 decompile, found that after all become 1, will perform sub_457AB4 () function:

 

Follow it:

Came sub_45E940 () function, found that key components:

Write a script to run it:

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}

Guess you like

Origin www.cnblogs.com/Theffth-blog/p/12231751.html