Mas0n
to be reverse engineer🐧
翻车鱼

“东华杯”2021年大学生网络安全邀请赛

“东华杯”2021年大学生网络安全邀请赛

上海省赛PY很严重……整体体验感很差。虽然说还是没能进前25,好在也不是一无所获:逆向AK了,算是初步了解了32位运行64位代码的模式和特征。

Hell’s Gate

32位exe

表面看上去是个RC4

https://cdn.shi1011.cn/2021/10/e0357b7bc3ad454e6571f298c103c138.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

然而钩子挂在了0x00416DFF

交叉引用定位到0x00416F90,这里有个反调试NtQueryInformationProcess,patch掉之后

https://cdn.shi1011.cn/2021/10/f0a6bdbe1702b3053609b588dd27444b.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

进入sub_4174C0后,会发现调用了一堆函数,这里就拿sub_4171B0分析

https://cdn.shi1011.cn/2021/10/f79434c18b725d07680125076994a9f3.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

会发现没法反编译,因为它是32位程序调用64位代码,具体可参考文章

CTF中32位程序调用64位代码的逆向方法

知道他是64位代码之后,采用静态分析

https://cdn.shi1011.cn/2021/10/ac7d1220dc25b85eecbc916296708562.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

修改PE64后,IDA64打开,Rebase Segment之后,再手动转换函数

https://cdn.shi1011.cn/2021/10/57451d5e9f3cdd45460783bac261a775.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

看到形如

void __fastcall __noreturn sub_171BF(int a1, int a2)
{
  void *retaddr[2]; // [rsp+8h] [rbp+0h]

  LODWORD(retaddr[1]) = a1;
  dword_20400[a1] = a2;
  JUMPOUT(0x23000893A9i64);
}

分析传参顺序和操作,会发现其实是基于栈的操作

https://cdn.shi1011.cn/2021/10/65bfda055d45a2cfd41e23e31dd2ec50.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5
// author: Mas0n
// Time: 2021/10/31 
// Desc: It's all about getting better.

#include <stdio.h>
#include <stdint.h>
#include <windows.h>
#include <string.h>


int main()
{
    DWORD dword_20400[768] = {0};
    char input_4203E0[96] = {"1111111111111111111111111111111"};
    dword_20400[0] = 0x12345678;
    dword_20400[1] = 0x87654321;
    dword_20400[2] = 0x13243546;
    dword_20400[3] = 0x64534231;

    *((DWORD*)(input_4203E0 + 32)) = dword_20400[0];
    *((DWORD*)(input_4203E0 + 40)) = dword_20400[1];
    *((DWORD*)(input_4203E0 + 48)) = dword_20400[2];
    *((DWORD*)(input_4203E0 + 56)) = dword_20400[3];

    dword_20400[0] = 0xB879379E;
    *((DWORD*)(input_4203E0 + 64)) = dword_20400[0];

    for (int j = 0; j < 32; j += 8) {
        dword_20400[0] = *((DWORD*)(input_4203E0 + j));
        dword_20400[1] = *((DWORD*)(input_4203E0 + j + 4));

        *((DWORD*)(input_4203E0 + 72)) = dword_20400[0];
        *((DWORD*)(input_4203E0 + 80)) = dword_20400[1];

        dword_20400[0] = 0x00000000;
        *((DWORD*)(input_4203E0 + 88)) = dword_20400[0];

        for (int k = 0; k < 16; ++k) {
            dword_20400[0] = *((DWORD*)(input_4203E0 + 88));
            dword_20400[1] = *((DWORD*)(input_4203E0 + 64));
            dword_20400[0] += dword_20400[1];
            *((DWORD*)(input_4203E0 + 88)) = dword_20400[0];
            dword_20400[0] = *((DWORD*)(input_4203E0 + 80));
            dword_20400[0] <<= 4;
            dword_20400[1] = *((DWORD*)(input_4203E0 + 32));
            dword_20400[0] += dword_20400[1];
            dword_20400[1] = *((DWORD*)(input_4203E0 + 80));
            dword_20400[2] = *((DWORD*)(input_4203E0 + 88));
            dword_20400[1] += dword_20400[2];
            dword_20400[2] = *((DWORD*)(input_4203E0 + 80));
            dword_20400[2] >>= 5;
            dword_20400[3] = *((DWORD*)(input_4203E0 + 40));
            dword_20400[2] += dword_20400[3];
            dword_20400[1] ^= dword_20400[2];
            dword_20400[0] ^= dword_20400[1];
            dword_20400[1] = *((DWORD*)(input_4203E0 + 72));
            dword_20400[1] += dword_20400[0];
            *((DWORD*)(input_4203E0 + 72)) = dword_20400[1];
            dword_20400[0] = *((DWORD*)(input_4203E0 + 72));
            dword_20400[0] <<= 4;
            dword_20400[1] = *((DWORD*)(input_4203E0 + 48));
            dword_20400[0] += dword_20400[1];
            dword_20400[1] = *((DWORD*)(input_4203E0 + 72));
            dword_20400[2] = *((DWORD*)(input_4203E0 + 88));
            dword_20400[1] += dword_20400[2];
            dword_20400[2] = *((DWORD*)(input_4203E0 + 72));
            dword_20400[2] >>= 5;
            dword_20400[3] = *((DWORD*)(input_4203E0 + 56));
            dword_20400[2] += dword_20400[3];
            dword_20400[1] ^= dword_20400[2];
            dword_20400[0] ^= dword_20400[1];
            dword_20400[1] = *((DWORD*)(input_4203E0 + 80));
            dword_20400[1] += dword_20400[0];
            *((DWORD*)(input_4203E0 + 80)) = dword_20400[1];
        }

        dword_20400[0] = *((DWORD*)(input_4203E0 + 72));
        dword_20400[1] = *((DWORD*)(input_4203E0 + 80));
        *((DWORD*)(input_4203E0 + j)) = dword_20400[0];
        *((DWORD*)(input_4203E0 + j + 4)) = dword_20400[1];
    }

    for (int i = 0; i < 32; i += 1) {
        printf("%x", input_4203E0[i]);
    }

    return 0;
}

抄下来,GCC重编译,放进IDA,呈现如下

strcpy(v10, "1111111111111111111111111111111");
  v11 = 0x12345678i64;
  v12 = 0x87654321i64;
  v13 = 0x13243546i64;
  v14 = 0x64534231i64;
  v15 = 0xB879379Ei64;
  v16 = 0i64;
  v17 = 0i64;
  v18 = 0i64;
  v3 = (__int64 *)v10;
  v4 = v10;
  do
  {
    v5 = *(_DWORD *)v4;
    v6 = *((_DWORD *)v4 + 1);
    v7 = v15;
    v8 = 16;
    do
    {
      v5 += (v12 + (v6 >> 5)) ^ (v6 + v7) ^ (v11 + 16 * v6);
      v6 += (v5 + v7) ^ (v14 + (v5 >> 5)) ^ (v13 + 16 * v5);
      v7 += v15;
      --v8;
    }
    while ( v8 );
    LODWORD(v18) = 16 * v15;
    LODWORD(v17) = v6;
    LODWORD(v16) = v5;
    *(_DWORD *)v4 = v5;
    *((_DWORD *)v4 + 1) = v6;
    v4 += 8;
  }
  while ( v4 != (char *)&v11 );
  do
  {
    printf("%x", (unsigned int)*(char *)v3);
    v3 = (__int64 *)((char *)v3 + 1);
  }
  while ( v3 != &v11 );

显然tea,撸脚本解

// author: Mas0n
// Time: 2021/10/31 
// Desc: It's all about getting better.

#include <stdio.h>
#include <stdint.h>
#include <windows.h>
#include <string.h>


void decrypt (DWORD* v) {
    unsigned int v4; // ecx
    unsigned int v5; // edx
    int v6; // er8
    DWORD key0; // [rsp+40h] [rbp-68h] BYREF
    DWORD key1; // [rsp+48h] [rbp-60h]
    DWORD key2; // [rsp+50h] [rbp-58h]
    DWORD key3; // [rsp+58h] [rbp-50h]
    key0 = 0x12345678;
    key1 = 0x87654321;
    key2 = 0x13243546;
    key3 = 0x64534231;

    int round = 0;
    DWORD sum = 0;


    while (round < 4) {
        sum = 0;
        for (int i = 0; i < 16; ++i) {
            sum += 0xB879379E;
        }
        v6 = 16;
        v4 = v[round*2];
        v5 = v[round*2+1];
        do
        {

            v5 -= (v4 + sum) ^ (key3 + (v4 >> 5)) ^ (key2 + 16 * v4);
            v4 -= (v5 + sum) ^ (key1 + (v5 >> 5)) ^ (key0 + 16 * v5);
            sum -= 0xB879379E;
            --v6;
        }
        while ( v6 );

        v[round*2] = v4;
        v[round*2+1] = v5;
        round += 1;
    }

}


int main()
{

    DWORD unk_41F8B0[8] = {
            0x2C94650B, 0x78494E9E, 0xE7FACF44, 0x48F9DBFB, 0x547BB145, 0x925D2542, 0x69A9F4C4, 0x9A96A1D8
    };

    decrypt(unk_41F8B0);
    for (int i = 0; i < 8; ++i) {
        printf("0x%lx, ", unk_41F8B0[i]);
    }
    printf("\n");
    return 0;
}

hello

Android逆向,Java层需要关注的函数new hi().getSignatures(view)

if (MainActivity.this.input.getText().length() == 42) {
                        Toast makeText = Toast.makeText(MainActivity.this, MainActivity.this.stringFromJNI(MainActivity.this.input.getText().toString(), new hi().getSignatures(view)), 1);
                        makeText.setGravity(0, 0, -700);
                        makeText.show();
                        return;
                    }

adb install -t hello.apk,logcat直接白给

public String getSignatures(View view) throws PackageManager.NameNotFoundException, NoSuchAlgorithmException {
        MessageDigest.getInstance("MD5");
        Signature[] signatureArr = view.getContext().getPackageManager().getPackageInfo(BuildConfig.APPLICATION_ID, 64).signatures;
        if (signatureArr.length <= 0) {
            return "this_is_your_gift!";
        }
        Signature signature = signatureArr[0];
        Log log = this.Log;
        Log.i("hello", signature.toCharsString()); // logcat
        return signature.toCharsString();
    }

ndk定位函数导出函数Java_com_example_hello_MainActivity_stringFromJNI

做了2种变换

第一部分简单的用得到的hash异或

  arr = (*a1)->GetStringUTFChars(a1, input, 0LL);
  hash = (*a1)->GetStringUTFChars(a1, a4, 0LL);
  if ( *arr )
  {
    hash2 = hash;
    *arr ^= hash[327];
    if ( strlen(arr) >= 2uLL )
    {
      v9 = 1LL;
      v10 = 354;
      do
      {
        arr[v9] ^= hash2[v10] + v9;
        ++v9;
        v10 += 27;
      }
      while ( strlen(arr) > v9 );
    }
  }

第二部分简单的位运算

  v11 = (arr[32] >> 3) & 0xFFFFE01F | (0x20 * arr[32]);
  v12 = (arr[33] >> 3) & 0x1F | (0x20 * arr[33]);
  v13 = (arr[35] >> 3) & 0xFFFFE01F | (32 * arr[35]);
  v14 = (arr[36] >> 3) & 0xFFFFE01F | (32 * arr[36]);
  v15 = (arr[37] >> 3) & 0xFFFFE01F | (32 * arr[37]);
  v16 = (arr[38] >> 3) & 0xFFFFE01F | (32 * arr[38]);
  v17 = (arr[39] >> 3) & 0xFFFFE01F | (32 * arr[39]);
  v18 = vorrq_s8(vshrq_n_u8(*arr, 3uLL), vshlq_n_s8(*arr, 5uLL));
  v19 = (arr[40] >> 3) & 0xFFFFE01F | (32 * arr[40]);
  v20 = vorrq_s8(vshrq_n_u8(*(arr + 1), 3uLL), vshlq_n_s8(*(arr + 1), 5uLL));
  v21 = (arr[41] >> 3) & 0xFFFFE01F | (32 * arr[41]);
  arr[34] = (arr[34] >> 3) & 0x1F | (32 * arr[34]);
  arr[32] = v11;
  arr[33] = v12;
  arr[35] = v13;
  arr[36] = v14;
  arr[37] = v15;
  arr[38] = v16;
  arr[39] = v17;
  arr[40] = v19;
  arr[41] = v21;

上脚本

# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: dhb.py
@Time: 2021-10-31 11:54
@Desc: It's all about getting better.
"""

arr = [0xCA, 0xEB, 0x4A, 0x8A, 0x68, 0xE1, 0xA1, 0xEB, 0xE1, 0xEE, 0x6B, 0x84, 0xA2, 0x6D, 0x49, 0xC8, 0x8E, 0x0E, 0xCC, 0xE9, 0x45, 0xCF, 0x23, 0xCC, 0xC5, 0x4C, 0x0C, 0x85, 0xCF, 0xA9, 0x8C, 0xF6, 0xE6, 0xD6, 0x26, 0x6D, 0xAC, 0x0C, 0xAC, 0x77, 0xE0, 0x64]
hash = bytearray(b"308202e4308201cc020101300d06092a864886f70d010105050030373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b30090603550406130255533020170d3231303330363134333034385a180f32303531303232373134333034385a30373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b300906035504061302555330820122300d06092a864886f70d01010105000382010f003082010a0282010100cbf2b09e4308ebb459e8841e5a7b920497fef2b349e80648f7eb35f48d40a75e7ce7945b8b42d197bec0bf177e6c9899ed707dcc4a726cb14c1a69b0c4a02474806fa73cfb10e10f7b1665021c24762b6edad65ca63cea3c72e0d4e4ca3f98301173eec3254337af1f5a11f779ecbe04d1b74d53f5835e011222155a56f97e00d75374cd93080dfa087cd356a99fe1eebf5d6d5e31846aad5252c3a17a4656e2e210ce1c7aa4d147fb8cf440a50add61bbb2ec299a2e0dab0b4504796ac3a899da553ab1d83576691ab23409d18398014b3b5eaf12e83f4d99aa09e1e4e4cae133530730c1133da2b3dee37b58eb1a5795b221ec5a8830731a41167d295f9e1b0203010001300d06092a864886f70d010105050003820101000e4740235e9cf2be33de3e06d777139cbbc5cf0622285c17da04697b8067318aaf8df0fbb4d3166f293ea15aa2592f06eb6929af063722ac9f30ad85e2c087564931d6ac65fcd5fbc864b3dc9841e039c6e1d5fbc5c2f8adf90a547bc4ebc07d387914db24451c2cc89925359bd3bb0750c7aabf9d743b1893e98bbc8ff74b24fc0b4be2dbaaf1c917bba01496d0617ffc3a4a8b7a6e79a3036298a6ebf57bb00001e43a0b242864eebb0fcec9e323144d4447c878430f18e6e358ad97566fa04d1f07b171c1476c9af5a1eba0bf6616e219c0b9e1299d09fecded24a880397f92e0f99d8951228c7770c184fd77adff943bfc8b6aa524c5f0a6d7686fe35486")
# t = 55
# t = (t >> 3) & 0x1F | (t << 5)
# t &= 0xff
# print(t)
# t |= (t & 0xf) << 8
# t = (t << 3) & 0x1F | (t >> 5)
# t &= 0xff
# print(t)

for i in range(0, 42):
    arr[i] |= (arr[i] & 0xf) << 8

for i in range(0, 42):
    arr[i] = ((arr[i] << 3) | (arr[i] >> 5)) & 0xff

for i in range(0, 42):
    arr[i] ^= hash[i * 27 + 327] + i
    print(i, chr(arr[i]))
print("".join([chr(i) for i in arr]))

ooo

简单异或

异或key为第7,8,9位字符异或

https://cdn.shi1011.cn/2021/10/1c18a06b8adca553719dc63884cd1c67.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

直接爆破

# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: dhb3.py
@Time: 2021-10-31 17:18
@Desc: It's all about getting better.
"""


data = [0x00000006, 0x0000010C, 0x00000201, 0x00000307, 0x0000041B, 0x00000551, 0x00000653, 0x00000706, 0x00000853, 0x00000955, 0x00000A56, 0x00000B56, 0x00000C53, 0x00000D4D, 0x00000E55, 0x00000F50, 0x00001001, 0x00001154, 0x0000124D, 0x00001354, 0x00001457, 0x00001557, 0x00001602, 0x0000174D, 0x00001852, 0x00001957, 0x00001A58, 0x00001B02, 0x00001C4D, 0x00001D02, 0x00001E57, 0x00001F51, 0x00002051, 0x00002150, 0x00002252, 0x00002356, 0x00002406, 0x00002506, 0x00002657, 0x00002701, 0x00002804, 0x0000291D]
for m in range(127):
    arr = [chr((j ^ m) & 0xff) for j in data]
    flag = "".join(arr)
    if flag.startswith("flag{"):
        print(flag)

mod

魔改base64

https://cdn.shi1011.cn/2021/10/78d1e2d490719c3bfc61ad8b4380f485.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

去除花指令后

int __cdecl sub_491320(int a1)
{
  int result; // eax
  char *v2; // [esp+10h] [ebp-2Ch]
  char *v3; // [esp+14h] [ebp-28h]
  signed int v4; // [esp+1Ch] [ebp-20h]
  int i; // [esp+28h] [ebp-14h]

  v4 = strlen(input);
  dword_4954A0 = v4;
  if ( v4 % 3 == 1 )
  {
    v3 = &input[dword_4954A0++];
    sub_491020(v3, "=");
  }
  else if ( v4 % 3 != 2 )
  {
    goto LABEL_6;
  }
  v2 = &input[dword_4954A0++];
  sub_491020(v2, "=");
LABEL_6:
  for ( i = 0; i < dword_4954A0; i += 3 )
    sub_4911A0((int)input, i, a1); // 主逻辑
  result = v4 / 3;
  if ( v4 % 3 == 1 )
    return sub_491020((char *)(a1 + 4 * (v4 / 3)), "==");
  if ( v4 % 3 == 2 )
    return sub_491020((char *)(a1 + 4 * (v4 / 3)), "=");
  return result;
}

主逻辑在sub_4911A0

https://cdn.shi1011.cn/2021/10/eef1924c03a52d8dc4aeaad50cb56667.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

上脚本,z3求解

# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: test14.py
@Time: 2021-10-31 15:47
@Desc: It's all about getting better.
"""
import z3
aAbcdfegh1jklrs = 'ABCDFEGH1JKLRSTMNP0VWQUXY2a8cdefijklmnopghwxyqrstuvzOIZ34567b9+/'
a3 = "2aYcdfL2fS1BTMMF1RSeMTTASS1OJ8RHTJdBYJ2STJfNMSMAYcKUJddp"
flag = ""
for i in range(0, len(a3), 4):
    i0 = aAbcdfegh1jklrs.index(a3[i + 0])
    i1 = aAbcdfegh1jklrs.index(a3[i + 1])
    i2 = aAbcdfegh1jklrs.index(a3[i + 2])
    i3 = aAbcdfegh1jklrs.index(a3[i + 3])
    a1 = [z3.BitVec("a{}".format(j), 8) for j in range(3)]

    sol = z3.Solver()
    sol.add(((4 * (a1[2] & 3)) | a1[1] & 0x30 | a1[0] & 0xC0) == i0 << 2)
    sol.add(((4 * (a1[0] & 3)) | a1[2] & 0x30 | a1[1] & 0xC0) == i1 << 2)
    sol.add(((4 * (a1[1] & 3)) | a1[0] & 0x30 | a1[2] & 0xC0) == i2 << 2)
    sol.add((a1[2] & 12 | (4 * a1[1]) & 0x30 | (16 * a1[0]) & 0xC0) == i3 << 2)

    assert sol.check() == z3.sat
    solve = sol.model()
    flag += "".join([chr(solve.eval(j).as_long()) for j in a1])

print(flag)




# a3[4 * (i / 3)] = aAbcdfegh1jklrs[((4 * (a1[i + 2] & 3)) | a1[i + 1] & 0x30 | a1[i] & 0xC0) >> 2]
# a3[4 * (i / 3) + 1] = aAbcdfegh1jklrs[((4 * (a1[i] & 3)) | a1[i + 2] & 0x30 | a1[i + 1] & 0xC0) >> 2]
# a3[4 * (i / 3) + 2] = aAbcdfegh1jklrs[((4 * (a1[i + 1] & 3)) | a1[i] & 0x30 | a1[i + 2] & 0xC0) >> 2]
# a3[4 * (i / 3) + 3] = aAbcdfegh1jklrs[(a1[i + 2] & 12 | (4 * a1[i + 1]) & 0x30 | (16 * a1[i]) & 0xC0) >> 2]

本文链接:https://blog.shi1011.cn/ctf/1750
本文采用 CC BY-NC-SA 4.0 Unported 协议进行许可

Mas0n

文章作者

回复 Cu6e 取消回复

textsms
account_circle
email

  • Cu6e

    师傅请教一下mod的z3求解过程中,我将”i0 <>2”就会有两组数据无解,是为什么呀?

    2年前 回复

翻车鱼

“东华杯”2021年大学生网络安全邀请赛
上海省赛PY很严重……整体体验感很差。虽然说还是没能进前25,好在也不是一无所获:逆向AK了,算是初步了解了32位运行64位代码的模式和特征。 Hell's Gate 32位exe 表面看上去是个…
扫描二维码继续阅读
2021-11-01