re

发布时间 2023-12-02 16:33:40作者: y2xsec

BUUCTF-RE

这里是buuctf平台的逆向的练习,我会在这里发布一些关于buuctf逆向题的解题思路,算是学习吧。因为我二进制不是很好,所以抽了点时间来做一些题来学习。

0x01 easyre

签到题,idea打开就看到flag了。

flag:flag{this_Is_a_EaSyRe}

0x02 reverse1

ida打开文件就看flag了,也是一道签到

flag:flag{hello_world}

0x03 reverse2

ida打开,找到main函数,关键代码:

    for ( i = 0; i <= strlen(&flag); ++i )
    {
      if ( *(&flag + i) == 105 || *(&flag + i) == 114 )
        *(&flag + i) = 49;
    }
  }

做了一个简单的替换,写脚本跑出来就可以了:

flag = list('hacking_for_fun}')
flag2 = []
for i in range(len(flag)):
    if ord(flag[i]) - i == 105 or ord(flag[i]) - i == 114:
        flag2.append(chr(49))
    else:
        flag2.append(flag[i])
print("".join(flag2))

# hacki1g_for_fun}

flag:flag{hacki1g_for_fun}

0x04 内涵的软件

查看字符串 View -> Open subviews -> Strings 就可以看到flag了。

其实函数是在_main_0函数里,直接找也可以找得到。

flag:flag{49d3c93df25caad81232130f3d2ebfad}

0x05 新年快乐

查个壳(其实做题之前应该要先查壳的,前几题看在是签到的份上就直接看了),ida打开后发现只有几个函数,于是丢到exeinfo里查壳。利用UPX Tool脱壳。

找到_main函数就可以看到HappyNewYear!了,可以查找字符串。

这里也可以用od进行脱壳(但是我不太会,正在学习ing)。

flag:flag{HappyNewYear!}

0x06 xor

查看发现没加壳,丢ida,主函数反编译

关键代码:

循环里做的是异或操作,前一位跟后一位进行异或然后赋值给后一位。

  for ( i = 1; i < 33; ++i )
    v5[i] ^= v5[i - 1];
  if ( !strncmp(v5, global, 0x21uLL) )
    printf("Success");

strncmp函数是比较v5和global变量,0x21uLL转换为十进制是33,也就是比较前33个字符是否相等。

于是点进global变量发现值如下:

f\nk\fw&O.@\x11x\rZ;U\x11p\x19F\x1Fv\"M#D\x0Eg\x06h\x0FG2O

exp:

global1 = list('f\nk\fw&O.@\x11x\rZ;U\x11p\x19F\x1Fv\"M#D\x0Eg\x06h\x0FG2O')
flag = []
for i in range(1,len(global1)):
    flag.append(chr(ord(global1[i]) ^ ord(global1[i-1])))
print("".join(flag))

# lag{QianQiuWanDai_YiTongJiangHu}

结果少了个f不晓得为啥,补上去就好了。

flag:flag{QianQiuWanDai_YiTongJiangHu}

0x07 helloword

android题目,利用jadx软件打开。

com.example.helloword -> MainActivity 就可以看到flag了。

flag:flag{7631a988259a00816deda84afb29430a}

0x08 reverse3

查壳发现没壳,找到主函数反编译。

从下往上看发现做了一次比较

strncmp(Destination, Str2, v5);

Str2和Destination进行了比较,点进Str2,值如下:

e3nifIH9b_C@n@dH

然后做了移位操作后,在往上就看到了sub_4110BE函数,进去看看是干嘛的。

代码挺复杂的,没看懂,但是发现了一个函数aAbcdefghijklmn,点进去就看到了编码表,于是就联想到了base64。

所以代码做的操作是:输入flag -> base64加密 -> 移位操作

编写exp:

import base64
d = 'e3nifIH9b_C@n@dH'
flag = []
# print(len(d))
for i in range(len(d)):
    flag.append(chr(ord(d[i])-i))
flag = "".join(flag)
print(base64.b64decode(flag))

# b'{i_l0ve_you}'

flag:flag{i_l0ve_you}

0x09 不一样的flag

迷宫题

迷宫如下:
*1111
01000
01010
00010
1111#

迷宫只能走0,1是不能走的
答案就是:222441144222

flag:flag{222441144222}

0x0A SimpleRev

大小端存储

v9 = 0x776F646168LL
# 

src = 0x534C43444ELL

可以看到这里的两个十六进制是大端序,但是数据在内存中都是小端序,所以要将其,反转一下。一般在CPU,x86都是小端序,但是IDA将之转换为了大端序。

v3 = 0
v2 = 0
v5 = 10
text = list("killshadow")
key = list("adsfkndcls")
str2 = []
for i in range(len(text)):
    for j in range(128):
        if chr(j).isalpha():
            k = (j - 39 - ord(key[i]) + 97) % 26 + 97
            if text[i] == chr(k):
                str2.append(chr(j))
                break
print("flag{" + "".join(str2) + "}")

flag:flag{KLDQCUDFZO}

0x0B Java逆向解密

给了一个class文件,用java反编译工具jd-gui打开,关键代码如下:

  public static void Encrypt(char[] arr) {
    ArrayList<Integer> Resultlist = new ArrayList<>();
    for (int i = 0; i < arr.length; i++) {
      int result = arr[i] + 64 ^ 0x20;
      Resultlist.add(Integer.valueOf(result));
    } 
    int[] KEY = { 
        180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 
        133, 191, 134, 140, 129, 135, 191, 65 };
    ArrayList<Integer> KEYList = new ArrayList<>();
    for (int j = 0; j < KEY.length; j++)
      KEYList.add(Integer.valueOf(KEY[j])); 
    System.out.println("Result:");
    if (Resultlist.equals(KEYList)) {
      System.out.println("Congratulations);
    } else {
      System.err.println("Error);
    } 
  }
}

做了异或和加减操作,代码很简单,做完操作跟KEY比较。

给出解题代码exp:

payload = [180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65]
flag = []
for i in payload:
    flag.append(chr((i-64)^32))
print("flag{" + "".join(flag) + "}")

# flag{This_is_the_flag_!}

flag:flag{This_is_the_flag_!}

0x0C [GXYCTF2019]luck_guy

丢ida反编译,找到关键函数get_flag,F5反编译,关键代码:

case 1:
        puts("OK, it's flag:");
        memset(&s, 0, 0x28uLL);
        strcat((char *)&s, f1);
        strcat((char *)&s, &f2);
        printf("%s", (const char *)&s);
        break;
        
case 4:
        s = 0x7F666F6067756369LL;
        v5 = 0;
        strcat(&f2, (const char *)&s);
        
case 5:
        for ( j = 0; j <= 7; ++j )
        {
          if ( j % 2 == 1 )
            *(&f2 + j) -= 2;
          else
            --*(&f2 + j);
        }
        break;

case1的f1变量的值是GXY{do_not_,直接点开就看到了,主要是f2的值,case4提到了f2的值是由s拼接的,根据上面讲到的小端存储,所以s的值应该是倒过来的,然后f2又出现在case5,经过简单的移位运算得到了f2.

分析完后编写脚本,exp:

flag1 = 'GXY{do_not_'
flag2 = ''
payload = [0x7F,0x66,0x6F,0x60,0x67,0x75,0x63,0x69]
payload.reverse()
f2 = ""
for i in payload:
    f2 += chr(i)
f3 = list(f2)

for j in range(8):
    if j % 2 == 1:
        f3[j] = ord(f3[j]) - 2
    else:
        f3[j] = ord(f3[j]) - 1

for k in f3:
    flag2 += chr(k)
flag = flag1 + flag2
print(flag)

# GXY{do_not_hate_me}

flag:flag{do_not_hate_me}

0x0D [BJDCTF2020]JustRE

ida反编译发现了DialogFunc函数,关键代码:

sprintf(String, " BJD{%d%d2069a45792d233ac}", 19999, 0);

flag直接给了,直接提交就好了

flag:flag{1999902069a45792d233ac}

0x0E 简单注册器

下载后发现是一个apk文件,丢到jadx里反编译。查看主函数,关键代码:

public void onClick(View v) {
                int flag = 1;
                String xx = editview.getText().toString();
                flag = (xx.length() == 32 && xx.charAt(31) == 'a' && xx.charAt(1) == 'b' && (xx.charAt(0) + xx.charAt(2)) + (-48) == 56) ? 0 : 0;
                if (flag == 1) {
                    char[] x = "dd2940c04462b4dd7c450528835cca15".toCharArray();
                    x[2] = (char) ((x[2] + x[3]) - 50);
                    x[4] = (char) ((x[2] + x[5]) - 48);
                    x[30] = (char) ((x[31] + x[9]) - 48);
                    x[14] = (char) ((x[27] + x[28]) - 97);
                    for (int i = 0; i < 16; i++) {
                        char a = x[31 - i];
                        x[31 - i] = x[i];
                        x[i] = a;
                    }
                    String bbb = String.valueOf(x);
                    textview.setText("flag{" + bbb + "}");
                    return;
                }

这个代码其实就是混淆,条件要flag等于1才能执行,只需要把下面代码删掉就可以了

flag = (xx.length() == 32 && xx.charAt(31) == 'a' && xx.charAt(1) == 'b' && (xx.charAt(0) + xx.charAt(2)) + (-48) == 56) ? 0 : 0;

因为他直接给了exp这里我就不写了照搬就是了,exp如下:

public class test1 implements Serializable {
    public static void main(String[] args) {
        int flag = 1;
        if (flag == 1) {
            char[] x = "dd2940c04462b4dd7c450528835cca15".toCharArray();
            x[2] = (char) ((x[2] + x[3]) - 50);
            x[4] = (char) ((x[2] + x[5]) - 48);
            x[30] = (char) ((x[31] + x[9]) - 48);
            x[14] = (char) ((x[27] + x[28]) - 97);
            for (int i = 0; i < 16; i++) {
                char a = x[31 - i];
                x[31 - i] = x[i];
                x[i] = a;
            }
            String bbb = String.valueOf(x);
            System.out.println(bbb);
        }
    }
}

0x0F [GWCTF 2019]pyre

python反编译,可以去在线网站直接反编译,也可以用cmake来反编译(推荐)

反编译后得到代码:

print 'Welcome to Re World!'
print 'Your input1 is your flag~'
l = len(input1)
for i in range(l):
    num = ((input1[i] + i) % 128 + 128) % 128
    code += num

for i in range(l - 1):
    code[i] = code[i] ^ code[i + 1]

print code
code = ['\x1f','\x12','\x1d','(','0','4','\x01','\x06','\x14','4',',','\x1b','U','?','o','6','*',':','\x01','D',';','%','\x13']

先进行的加减取余运算,再进行异或,逆一下就可以解出来了,exp如下:

code = ['\x1f','\x12','\x1d','(','0','4','\x01','\x06','\x14','4',',','\x1b','U','?','o','6','*',':','\x01','D',';','%','\x13']
flag = ""
for i in range(len(code)-2,-1,-1):
    code[i] = chr(ord(code[i])^ord(code[i+1]))
for i in range(len(code)):
    flag+=chr((ord(code[i])-i)%128)
print(flag)

flag:flag{Just_Re_1s_Ha66y!}

0x10 [ACTF新生赛2020]easyre

下载得到一个apk文件,直接丢到jadx反编译,关键代码:

        final char[] a = {'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'};
        final char[] b = {'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'};
        btn.setOnClickListener(new View.OnClickListener() { // from class: com.example.findit.MainActivity.1
            @Override // android.view.View.OnClickListener
            public void onClick(View v) {
                char[] x = new char[17];
                char[] y = new char[38];
                for (int i = 0; i < 17; i++) {
                    if ((a[i] < 'I' && a[i] >= 'A') || (a[i] < 'i' && a[i] >= 'a')) {
                        x[i] = (char) (a[i] + 18);
                    } else if ((a[i] >= 'A' && a[i] <= 'Z') || (a[i] >= 'a' && a[i] <= 'z')) {
                        x[i] = (char) (a[i] - '\b');
                    } else {
                        x[i] = a[i];
                    }
                }

第一个函数就是对a数组进行一个运算,然后条件进行比对,最后进到最后一个函数输出flag。其实直接照着写函数输出就可以了,但是我在写第二个函数的时候发现运行不出来flag,于是就选择把apk文件装到模拟器里运行,然后把第一个函数运行出来的结果丢进去就得到flag了。

a = ['T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e']
x = []

for i in range(17):
    if (a[i] < 'I' and a[i] >= 'A') or (a[i] < 'i' and a[i] >= 'a'):
        x.append(chr(ord(a[i])+18))
    elif ((a[i] >= 'A' and a[i] <= 'Z') or (a[i] >= 'a' and a[i] <= 'z')):
        x.append(chr(ord(a[i])-ord('\b')))
    else:
        x.append(a[i])
# x = LzakAkLzwXdsyZgew

flag:flag{c164675262033b4c49bdf7f9cda28a75}

0x11 [FlareOn4]login

rot18解密(主要是我懒得解,后面补上)

flag:flag{ClientSideLoginsAreEasy@flare-on.com}

0x12 [WUSTCTF2020]level1

拿到文件,丢到ida反编译,关键代码:

  stream = fopen("flag", "r");
  fread(ptr, 1uLL, 0x14uLL, stream);
  fclose(stream);
  for ( i = 1; i <= 19; ++i )
  {
    if ( (i & 1) != 0 )
      printf("%ld\n", (unsigned int)(ptr[i] << i));
    else
      printf("%ld\n", (unsigned int)(i * ptr[i]));
  }

将文件flag丢给ptr,然后进行运算,简单的逆向运算。

然后还给了output文件:

[198, 232, 816, 200, 1536, 300, 6144, 984, 51200, 570, 92160, 1200, 565248, 756, 1474560, 800, 6291456, 1782, 65536000]

# 太占地方了就写成这个样子了

exp如下:

a = ['0',198, 232, 816, 200, 1536, 300, 6144, 984, 51200, 570, 92160, 1200, 565248, 756, 1474560, 800, 6291456, 1782, 65536000]
b = []
for i in range(1,19):
    if ((i & 1) != 0):
        # 前:左移位;后:右移位
        a[i] = chr(a[i] >> i)
    else:
        # 前:i * ptr[i];后:ptr[i] / i
        a[i] = chr((int)(a[i]/i))
print(a)
# flag{d9-dE6-20c}

flag:flag{d9-dE6-20c}

0x13 [2019红帽杯]easyRE

丢到ida里,查找字符串的时候就发现了很像base64的东西,复制下来:

Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFpOYmtKVVZtcEtTMUl5VGtsaVJtUk9ZV3hhZVZadGVHdFRNVTVYVW01T2FGSnRVbGhhVjNoaFZWWmtWMXBFVWxSTmJFcElWbTAxVDJGV1NuTlhia0pXWWxob1dGUnJXbXRXTVZaeVdrWm9hVlpyV1hwV1IzaGhXVmRHVjFOdVVsWmlhMHBZV1ZSR1lWZEdVbFZTYlhSWFRWWndNRlZ0TVc5VWJGcFZWbXR3VjJKSFVYZFdha1pXWlZaT2NtRkhhRk5pVjJoWVYxZDBhMVV3TlhOalJscFlZbGhTY1ZsclduZGxiR1J5VmxSR1ZXSlZjRWhaTUZKaFZqSktWVkZZYUZkV1JWcFlWV3BHYTFkWFRrZFRiV3hvVFVoQ1dsWXhaRFJpTWtsM1RVaG9hbEpYYUhOVmJUVkRZekZhY1ZKcmRGTk5Wa3A2VjJ0U1ExWlhTbFpqUldoYVRVWndkbFpxUmtwbGJVWklZVVprYUdFeGNHOVhXSEJIWkRGS2RGSnJhR2hTYXpWdlZGVm9RMlJzV25STldHUlZUVlpXTlZadE5VOVdiVXBJVld4c1dtSllUWGhXTUZwell6RmFkRkpzVWxOaVNFSktWa1phVTFFeFduUlRhMlJxVWxad1YxWnRlRXRXTVZaSFVsUnNVVlZVTURrPQ== 

base64解密:

https://bbs.pediy.com/thread-254172.htm

访问后在评论区找到了flag

flag:flag{Act1ve_Defen5e_Test}

0x14 [GUET-CTF2019]re

下载文件,丢到exeinfo查壳,发现有upx壳,用工具脱壳也可以用kali自带的脱壳

upx -d [文件]

拿到脱壳后的文件丢到ida里分析,F5反编译后,看到有个sub_4009AE函数,进入后发现有很多的条件,这个其实可以手动的算条件里的a1,但是我看了一下wp说用z3更方便一点,于是就借鉴了一下,也算是学习了。

代码要注意的点是,没有a1[6],17和18是颠倒的。

z3约束器,就是解决方程的,exp如下:

from z3 import *
s = Solver()
a1 = [0]*31
for i in range(31):
    a1[i] = Int('a1['+str(i)+']')

s.add( 1629056 * a1[0] == 166163712 )
s.add( 6771600 * a1[1] == 731332800 )
s.add( 3682944 * a1[2] == 357245568 )
s.add( 10431000 * a1[3] == 1074393000 )
s.add( 3977328 * a1[4] == 489211344 )
s.add( 5138336 * a1[5] == 518971936 )
s.add( 7532250 * a1[6] == 406741500 )
s.add( 5551632 * a1[7] == 294236496 )
s.add( 3409728 * a1[8] == 177305856 )
s.add( 13013670 * a1[9] == 650683500 )
s.add( 6088797 * a1[10] == 298351053 )
s.add( 7884663 * a1[11] == 386348487 )
s.add( 8944053 * a1[12] == 438258597 )
s.add( 5198490 * a1[13] == 249527520 )
s.add( 4544518 * a1[14] == 445362764 )
s.add( 3645600 * a1[15] == 174988800 )
s.add( 10115280 * a1[16] == 981182160 )
s.add( 9667504 * a1[17] == 493042704 )
s.add( 5364450 * a1[18] == 257493600 )
s.add( 13464540 * a1[19] == 767478780 )
s.add( 5488432 * a1[20] == 312840624 )
s.add( 14479500 * a1[21] == 1404511500 )
s.add( 6451830 * a1[22] == 316139670 )
s.add( 6252576 * a1[23] == 619005024 )
s.add( 7763364 * a1[24] == 372641472 )
s.add( 7327320 * a1[25] == 373693320 )
s.add( 8741520 * a1[26] == 498266640 )
s.add( 8871876 * a1[27] == 452465676 )
s.add( 4086720 * a1[28] == 208422720 )
s.add( 9374400 * a1[29] == 515592000 )
s.add(5759124 * a1[30] == 719890500)
s.check()
a = s.model()
print(a)

运行完后拿到很多数据,写一个解密脚本:

a1 = [0]*32
a1[31] = 125
a1[30] = 55
a1[29] = 51
a1[28] = 51
a1[27] = 57
a1[26] = 51
a1[25] = 48
a1[24] = 99
a1[23] = 49
a1[22] = 97
a1[21] = 57
a1[20] = 57
a1[19] = 48
a1[18] = 51
a1[16] = 97
a1[17] = 48
a1[15] = 98
a1[14] = 48
a1[13] = 49
a1[12] = 49
a1[11] = 49
a1[10] = 50
a1[9] = 52
a1[8] = 53
a1[7] = 54
a1[5] = 101
a1[4] = 123
a1[3] = 103
a1[2] = 97
a1[1] = 108
a1[0] = 102
for i in range(32):
    if i == 6:
        continue
    print(chr(a1[i]), end="")
# flag{e?65421110ba03099a1c039337}

解出来发现少了一个数(也就是刚刚a1[6]那里),于是盲猜呗,0-F一个一个试过去就可以了。 最后拿到flag:

flag:flag{e165421110ba03099a1c039337}

0x15 [SUCTF2019]SignIn

下载文件丢ida反编译,从代码可以看出是RSA算法

 v11 = *MK_FP(__FS__, 40LL);
  puts("[sign in]");
  printf("[input your flag]: ", a2);
  __isoc99_scanf("%99s", &v9);
  sub_96A(&v9, (__int64)&v10);
  __gmpz_init_set_str((__int64)&v8, (__int64)"ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35", 16LL);
  __gmpz_init_set_str((__int64)&v7, (__int64)&v10, 16LL);
  __gmpz_init_set_str(
    (__int64)&v5,
    (__int64)"103461035900816914121390101299049044413950405173712170434161686539878160984549",
    10LL);
  __gmpz_init_set_str((__int64)&v6, (__int64)"65537", 10LL);
  __gmpz_powm((__int64)&v7, (__int64)&v7, (__int64)&v6, (__int64)&v5);
  if ( __gmpz_cmp((__int64)&v7, (__int64)&v8) )
    puts("GG!");
  else
    puts("TTTTTTTTTTql!");
  result = 0LL;
  v4 = *MK_FP(__FS__, 40LL) ^ v11;
  return result;

从上面分析来看,v5是n,v6是指数e,v7就是明文m,v8是密文c。

函数分析:

__gmpz_init_set_str(a,b,c):a是变量,b是值,c是进制
__gmpz_powm(a,b,c,d):四个参数,把b的c次方模d的值赋给a
__gmpz_cmp(a,b):a与b进行比较

根据RSA算法,可以先用factor分解一下n(n较小),也可以用网站分解,exp如下:

import gmpy2
from Crypto.Util.number import *

n = 103461035900816914121390101299049044413950405173712170434161686539878160984549
e = 65537
c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
p = 366669102002966856876605669837014229419
q = 282164587459512124844245113950593348271

phi = (p - 1) * (q - 1)
d = int(gmpy2.invert(e,phi))
m = pow(c,d,n)
print(long_to_bytes(m))

flag:flag{Pwn_@_hundred_years}

0x16 [MRCTF2020]Xor

ida分析不了,于是汇编分析:

.rdata:0041EA08 byte_41EA08     db 4Dh                  ; DATA XREF: _main+48↑r
.rdata:0041EA09 aSawbFxzJTqjNBp db 'SAWB~FXZ:J:`tQJ"N@ bpdd}8g',0

这里有一个参数,加上0x4D(也就是M),就是MSAWB~FXZ:J:`tQJ"N@ bpdd}8g。

分析汇编代码

# byte_4212C0存放的是flag
push    offset aGiveMeYourFlag ; "Give Me Your Flag String:\n"
call    sub_401020
push    64h ; 'd'
push    offset byte_4212C0
push    offset aS       ; "%s"
call    sub_401050
mov     edx, offset byte_4212C0
add     esp, 10h
lea     ecx, [edx+1]
	|
	|
# 循环
loc_4010B6:
mov     al, [edx]
inc     edx
test    al, al
jnz     short loc_4010B6
	|
	|
# 判断,不等于27就返回wrong(这里可以知道flag的长度是27)
# edx存放的是flag的长度
sub     edx, ecx
cmp     edx, 27
jnz     short loc_4010FF
	|
	|
xor     eax, eax
db      66h, 66h
nop     word ptr [eax+eax+00000000h]
	|
	|
# 异或运算,异或完进行比对
loc_4010D0:
mov     cl, byte_4212C0[eax]
xor     cl, al
cmp     cl, ds:byte_41EA08[eax]
jnz     short loc_4010FF
	|
	|
# eax自增,与edx(27)进行比对
inc     eax
cmp     eax, edx
jb      short loc_4010D0

分析完后编写exp:

a = list('MSAWB~FXZ:J:`tQJ"N@ bpdd}8g')
for i in range(len(a)):
    print(chr(ord(a[i])^i),end="")
    # print(hex(ord(a[i])))

flag:flag{@_R3@1ly_E2_R3verse!}