【CTF reverse】逆向入门题解集合2

文章目录

      • buu-简单注册器-安卓逆向
      • buu-不一样的flag
      • buu-SimpleRev-小端存储

作者:hans774882968以及hans774882968

buu-简单注册器-安卓逆向

上JEB

package com.example.flag;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View.OnClickListener;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity {
    public static class PlaceholderFragment extends Fragment {
        @Override  // android.support.v4.app.Fragment
        public View onCreateView(LayoutInflater arg4, ViewGroup arg5, Bundle arg6) {
            return arg4.inflate(0x7F030018, arg5, false);  // layout:fragment_main
        }
    }

    @Override  // android.support.v7.app.ActionBarActivity
    protected void onCreate(Bundle arg7) {
        super.onCreate(arg7);
        this.setContentView(0x7F030017);  // layout:activity_main
        if(arg7 == null) {
            this.getSupportFragmentManager().beginTransaction().add(0x7F05003C, new PlaceholderFragment()).commit();  // id:container
        }

        Button button = (Button)this.findViewById(0x7F05003F);  // id:button1
        TextView textview = (TextView)this.findViewById(0x7F05003E);  // id:textView1
        button.setOnClickListener(new View.OnClickListener() {
            @Override  // android.view.View$OnClickListener
            public void onClick(View arg13) {
                int flag = 1;
                String xx = ((EditText)this.findViewById(0x7F05003D)).getText().toString();  // id:editText1
                if(xx.length() != 0x20 || xx.charAt(0x1F) != 97 || xx.charAt(1) != 98 || xx.charAt(0) + xx.charAt(2) - 0x30 != 56) {
                    flag = 0;
                }

                if(flag == 1) {
                    char[] x = "dd2940c04462b4dd7c450528835cca15".toCharArray();
                    x[2] = (char)(x[2] + x[3] - 50);
                    x[4] = (char)(x[2] + x[5] - 0x30);
                    x[30] = (char)(x[0x1F] + x[9] - 0x30);
                    x[14] = (char)(x[27] + x[28] - 97);
                    int i;
                    for(i = 0; i < 16; ++i) {
                        char a = x[0x1F - i];
                        x[0x1F - i] = x[i];
                        x[i] = a;
                    }

                    textview.setText("flag{" + String.valueOf(x) + "}");
                    return;
                }

                textview.setText("输入注册码错误");
            }
        });
    }

    @Override  // android.app.Activity
    public boolean onCreateOptionsMenu(Menu arg3) {
        this.getMenuInflater().inflate(0x7F0C0000, arg3);  // menu:main
        return 1;
    }

    @Override  // android.app.Activity
    public boolean onOptionsItemSelected(MenuItem arg3) {
        return arg3.getItemId() == 0x7F050040 ? true : super.onOptionsItemSelected(arg3);  // id:action_settings
    }
}

用Java运行一下那段逻辑就行,但我还是把它改写为python了

x = list('dd2940c04462b4dd7c450528835cca15')
x[2] = chr(ord(x[2]) + ord(x[3]) - 50)
x[4] = chr(ord(x[2]) + ord(x[5]) - 0x30)
x[30] = chr(ord(x[0x1F]) + ord(x[9]) - 0x30)
x[14] = chr(ord(x[27]) + ord(x[28]) - 97)
print("flag{%s}" % "".join(x[::-1]))

buu-不一样的flag

放PETools,32位程序,无壳。

一眼就能看到main函数。

void main()
{
  char v0; // [sp+17h] [bp-35h]@1
  int v1; // [sp+30h] [bp-1Ch]@1
  int v2; // [sp+34h] [bp-18h]@1
  signed int v3; // [sp+38h] [bp-14h]@2
  signed int i; // [sp+3Ch] [bp-10h]@14
  int v5; // [sp+40h] [bp-Ch]@20

  __main();
  v1 = 0;
  v2 = 0;
  qmemcpy(&v0, _data_start__, 0x19u);
  while ( 1 )
  {
    puts("you can choose one action to execute");
    puts("1 up");
    puts("2 down");
    puts("3 left");
    printf("4 right\n:");
    scanf("%d", &v3);
    if ( v3 == 2 )
    {
      ++v1;
    }
    else if ( v3 > 2 )
    {
      if ( v3 == 3 )
      {
        --v2;
      }
      else
      {
        if ( v3 != 4 )
LABEL_13:
          exit(1);
        ++v2;
      }
    }
    else
    {
      if ( v3 != 1 )
        goto LABEL_13;
      --v1;
    }
    for ( i = 0; i <= 1; ++i )
    {
      if ( *(&v1 + i) < 0 || *(&v1 + i) > 4 )
        exit(1);
    }
    if ( *((_BYTE *)&v5 + 5 * v1 + v2 - 41) == '1' )
      exit(1);
    if ( *((_BYTE *)&v5 + 5 * v1 + v2 - 41) == '#' )
    {
      puts("\nok, the order you enter is the flag!");
      exit(0);
    }
  }
}

这里的v5变量比较奇怪,它在用于判定之前,从来没被使用过。所以不妨从地址入手分析。&v5 + 5 * v1 + v2 - 41显然是一个地址。v5在栈上的地址[sp+40h]减去41是什么,就是[sp+17h],也就是变量v0。然后v0就是_data_start__,也就是以下字符数组:

*1111
01000
01010
00010
1111#

所以我们找到了迷宫地图,直接写出路径就是flag。

buu-SimpleRev-小端存储

文件没有后缀名,用Linux的file命令检查:

SimpleRev: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=02be0b7299e735062807898251b7937713df2c41, not stripped

64位程序。用IDA很容易就找到了关键函数。下面对关键函数做了注释,很容易就能看懂。

__int64 Decry()
{
  char v1; // [sp+Fh] [bp-51h]@19
  int v2; // [sp+10h] [bp-50h]@1
  signed int v3; // [sp+14h] [bp-4Ch]@1
  signed int i; // [sp+18h] [bp-48h]@1
  signed int v5; // [sp+1Ch] [bp-44h]@1
  char src[8]; // [sp+20h] [bp-40h]@1
  __int64 v7; // [sp+28h] [bp-38h]@1
  int v8; // [sp+30h] [bp-30h]@1
  __int64 v9; // [sp+40h] [bp-20h]@1
  __int64 v10; // [sp+48h] [bp-18h]@1
  int v11; // [sp+50h] [bp-10h]@1
  __int64 v12; // [sp+58h] [bp-8h]@1

  v12 = *MK_FP(__FS__, 40LL);
  *(_QWORD *)src = 0x534C43444ELL;
  v7 = 0LL;
  v8 = 0;
  v9 = 0x776F646168LL;
  v10 = 0LL;
  v11 = 0;
  // join就是text = key3 + v9
  text = join(key3, (const char *)&v9);// key3 = 'kills', v9 = 'wodah'[::-1]
  strcpy(key, key1);// key1 = 'ADSFK'
  strcat(key, src);// src = 'SLCDN'[::-1]
  v2 = 0;
  v3 = 0;
  getchar();
  v5 = strlen(key);
  // 既然key和text串都确定了,就可以知道mod v5是干扰信息。
  for ( i = 0; i < v5; ++i )
  {
    if ( key[v3 % v5] > 64 && key[v3 % v5] <= 'Z' )//isupper()
      key[i] = key[v3 % v5] + 32;//所以只是tolower()
    ++v3;
  }
  // key = 'adsfkndcls'
  printf("Please input your flag:", src);
  while ( 1 )
  {
    v1 = getchar();
    if ( v1 == '\n' )
      break;
    if ( v1 == ' ' )
    {
      ++v2;
    }
    else
    {
      if ( v1 <= 96 || v1 > 'z' )//!islower()
      {
        if ( v1 > 64 && v1 <= 'Z' )//isupper()
          str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 'a';
      }
      else
      {
        str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 'a';
      }
      if ( !(v3 % v5) )
        putchar(32);
      ++v2;
    }
  }
  if ( !strcmp(text, str2) )
    puts("Congratulation!\n");
  else
    puts("Try again!\n");
  return *MK_FP(__FS__, 40LL) ^ v12;
}

容易忘记的事情是:小端存储无疑是主流,所以src和v9的long long用字符串来解释时,要反转一下(我就踩了这坑555)。

怎么反解:写成模26的同余方程即可。大写字母的情况很简单,ord(key[i]) - 97 + ord(text[i]) - 97;对于小写字母,减39等于减39 + 52 = 91,为了凑出'a',再减6,所以是ord(key[i]) - 97 + ord(text[i]) - 97 - 6

按道理来说这题有2^10个正确答案的,但是我们不知道是哪个,并且全大写的和全小写的都不是有意义的字符串。就交个全大写的和全小写的试试。试出来全大写那个是对的。

def getchs(s):
    ans = ''
    for i in range(0,len(s),2):
        ans += chr(int(s[i:i+2],16))
    return ans

src = getchs('534C43444E')[::-1]
v9 = getchs('776F646168')[::-1]
print(src,v9)
key = 'ADSFKNDCLS'.lower()
text = 'killshadow'
print(text,key)# key = 'adsfkndcls'
ans1,ans2 = '',''
for i in range(10):
    ans1 += chr(65 + (ord(key[i]) - 97 + ord(text[i]) - 97) % 26)
    ans2 += chr(97 + (20 + ord(key[i]) - 97 + ord(text[i]) - 97) % 26)
print(ans1)
print(ans2)

def jdg(s):
    for i in range(10):
        v = (ord(s[i]) - 39 - (ord(key[i]) - 97)) % 26
        if v != ord(text[i]) - 97: return False
    return True

print(jdg(ans1),jdg(ans2))#都是True

总结:易错点:忘记小端存储

你可能感兴趣的:(web&CTF,android,网络安全,python)