2024 极客大挑战re week1 wp
1438911687251843 发表于 江西 CTF 430浏览 · 2024-11-18 16:25

先来一道签到题

记事本打开,是一个纯汇编的文本

分析一下,对字符数组每两项的第一项先异或7,第二项异或5,写成C语言为

for (i = 0; i < 36; i += 2) {
        input[i] ^= 7; 
        input[i + 1] -= 5; 
    }

解密脚本

enc = 'TTDv^jrZu`Gg6tXfi+pZojpZSjXmbqbmt.&x'
for i in range(0,len(enc),2):
    print(chr(ord(enc[i])^7),end='')
    print(chr(ord(enc[i+1])+5),end='')

让我康康你的调试

这题调不调试没有区别,调试只是更容易取得数据

ida反编译

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int i; // [rsp+4h] [rbp-7Ch]
  char s1[57]; // [rsp+17h] [rbp-69h] BYREF
  __int64 s2[4]; // [rsp+50h] [rbp-30h] BYREF
  char v7; // [rsp+70h] [rbp-10h]
  unsigned __int64 v8; // [rsp+78h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  strcpy(s1, "syclover");
  s2[0] = 0xA67A02C9047D5B94LL;
  s2[1] = 0x7EF9680DBC980739LL;
  s2[2] = 0x7104F81698BFBD08LL;
  s2[3] = 0x61DB8498B686155FLL;
  v7 = 0x6D;
  puts("逆向的大门矗立在前方,相信你能打开");
  __isoc99_scanf("%33s", &s1[9]);
  for ( i = 0; i <= 32; ++i )
    s1[i + 9] ^= 20u;
  sub_5A15D0FDE4A6(&s1[9], 0x21uLL, s1, 8LL);
  if ( !memcmp(&s1[9], s2, 33uLL) )
    puts("恭喜你打开了逆向的大门,现在,开始你的旅程吧");
  else
    puts("也许打开的方式不太对,要不再来一次^_^");
  puts("Press Enter to exit...");
  getchar();
  getchar();
  return 0;
}

在puts这一行下断点,先调试取出s2的值

这个sub_5A15D0FDE4A6函数显然为RC4加密,直接丢进赛博厨子解密

然后还有一个异或操作

a = 0x474d576f637125777b79714b60244b5371717f33674b2775676d4b667720356914
v5 = 'syclover'
flag = []
for i in range(33):
    enc = a & 0xff
    a >>= 8
    flag.append(chr(enc^20))
flag.reverse()
print(''.join(flag))

ezzzz

apk文件,使用jeb反编译

apk的主函数为MainActivity

package com.example.ezzzz;

import android.os.Bundle;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    private Button btn;
    private EditText et;

    // Detected as a lambda impl.
    public void check() {
        String s = this.getResources().getString(string.target);
        if(Enc.encrypt(this.et.getText().toString()).equals(s)) {
            Toast.makeText(this, "Wow!You are right!!!", 0).show();
            return;
        }

        Toast.makeText(this, "Emmmmm……wrong????", 0).show();
    }

    @Override  // androidx.fragment.app.FragmentActivity
    protected void onCreate(Bundle bundle0) {
        super.onCreate(bundle0);
        this.setContentView(layout.activity_main);
        this.btn = (Button)this.findViewById(id.btn);
        this.et = (EditText)this.findViewById(id.et);
        this.btn.setOnClickListener((/* MISSING LAMBDA PARAMETER */) -> {
            String s = MainActivity.this.getResources().getString(string.target);
            if(Enc.encrypt(MainActivity.this.et.getText().toString()).equals(s)) {
                Toast.makeText(MainActivity.this, "Wow!You are right!!!", 0).show();
                return;
            }

            Toast.makeText(MainActivity.this, "Emmmmm……wrong????", 0).show();
        });

        class com.example.ezzzz.MainActivity.1 implements View.OnClickListener {
            @Override  // android.view.View$OnClickListener
            public void onClick(View view0) {
                MainActivity.this.check();
            }
        }

    }
}

在apk中,setOnClickListener一般为点击事件,下面的代码一般为判断逻辑,双击进入第34行的encrypt函数

package com.example.ezzzz;

public class Enc {
    private static final int DELTA = -1640531527;

    public static String encrypt(String s) {
        //创建新int数组arr_v存密文s
        int v = s.length();
        int[] arr_v = new int[v];   
        for(int v2 = 0; v2 < v; ++v2) {
            arr_v[v2] = s.charAt(v2);
        }
        //arr_v1数组存key
        int[] arr_v1 = new int[4];
        for(int v3 = 0; v3 < 4; ++v3) {
            arr_v1[v3] = "GEEK".charAt(v3);
        }
        //循环取值加密,一次取两个数据
        for(int v4 = 0; v4 < v; v4 += 2) {
            int[] arr_v2 = {arr_v[v4], arr_v[v4 + 1]};
            Enc.encrypt(arr_v2, arr_v1);
            arr_v[v4] = arr_v2[0];
            arr_v[v4 + 1] = arr_v2[1];
        }
        //给加密后的数组每个元素以十六进制形式补齐8位,不足补0,以字符形式存到stringBuilder
        StringBuilder stringBuilder0 = new StringBuilder();
        for(int v1 = 0; v1 < v; ++v1) {
            stringBuilder0.append(String.format("%08x", ((int)arr_v[v1])));
        }

        return stringBuilder0.toString();
    }
    //魔改xtea加密
    private static void encrypt(int[] arr_v, int[] arr_v1) {
        int v = arr_v[0];
        int v1 = arr_v[1];
        int v3 = 0;
        for(int v2 = 0; v2 < 0x20; ++v2) {
            v += (v1 << 4 ^ v1 >> 5) + v1 ^ arr_v1[v3 & 3] + v3 ^ v3 + v2;
            v3 -= 1640531527;
            v1 += (v << 4 ^ v >> 5) + v ^ arr_v1[v3 >>> 11 & 3] + v3 ^ v3 + v2;
        }

        arr_v[0] = v;
        arr_v[1] = v1;
    }
}

一眼能看出是xtea,这里要注意这是魔改的xtea,加密部分多异或了一个 (sum+ i),所以写逆向代码需要从32到0循环

秘钥为"GEEK",密文要回到主函数找

需要注意的是,在jar包里的java代码中,字符串资源都是以ID的形式存在的,在主函数找到密文s存在target变量中,双击target

绿色的就是资源ID,jeb还贴心地直接注释出了字符串资源

如果是使用jadx反编译,可以在左侧目录下资源文件/resources.arsc/res/values/strings.xml中找到

先把这个字符串还原成十六进制

a = 'f1f186b25a96c782e6c63a0b70b61b5ced6bf84889700d6b09381b5ccb2f24fab1c79e796d822d9cdcc55f760f780e750d65c4afb89084a9e978c3827a8dd81091f28df3a84dbacab4d75f75f19af8e5b90f80fcfc10a5c3d20679fb2bc734c8ccb31c921ac52ad3e7f922b72e24d923fb4ce9f53548a9e571ebc25adf38862e10059186327509463dd4d54c905abc36c26d5312d2cd42c0772d99e50cd4c4665c3178d63a7ffe71ada251c070568d5a5798c2921ec0f7fc3ae9d8418460762930ca6a2dccef51d2a1a8085491b0f82d686ca34774c52d0f0f26449fc28d362c86f3311b8adc4fb1a4497e34e0f0915d'
for i in range(0,len(a),8):
    print(f'0x{a[i]}{a[i+1]}{a[i+2]}{a[i+3]}{a[i+4]}{a[i+5]}{a[i+6]}{a[i+7]}',end=',')

然后写脚本

#include <stdio.h>

void decrypt(int* v, int* key) {
    int v0 = v[0], v1 = v[1], delta = -0x61C88647, sum = delta * 32;
    for (int i = 31; i >= 0; i--) {
        v1 -= ((v0 << 4) ^ (v0 >> 5)) + v0 ^ key[(sum >> 11) & 3] + sum ^ sum + i;
        sum += 0x61C88647;
        v0 -= ((v1 << 4) ^ (v1 >> 5)) + v1 ^ key[sum & 3] + sum ^ sum + i;
    }
    v[0] = v0; v[1] = v1;
}

int main() {
    int data[] = {
        0xf1f186b2,0x5a96c782,0xe6c63a0b,0x70b61b5c,0xed6bf848,0x89700d6b,0x09381b5c,
        0xcb2f24fa,0xb1c79e79,0x6d822d9c,0xdcc55f76,0x0f780e75,0x0d65c4af,0xb89084a9,
        0xe978c382,0x7a8dd810,0x91f28df3,0xa84dbaca,0xb4d75f75,0xf19af8e5,0xb90f80fc,
        0xfc10a5c3,0xd20679fb,0x2bc734c8,0xccb31c92,0x1ac52ad3,0xe7f922b7,0x2e24d923,
        0xfb4ce9f5,0x3548a9e5,0x71ebc25a,0xdf38862e,0x10059186,0x32750946,0x3dd4d54c,
        0x905abc36,0xc26d5312,0xd2cd42c0,0x772d99e5,0x0cd4c466,0x5c3178d6,0x3a7ffe71,
        0xada251c0,0x70568d5a,0x5798c292,0x1ec0f7fc,0x3ae9d841,0x84607629,0x30ca6a2d,
        0xccef51d2,0xa1a80854,0x91b0f82d,0x686ca347,0x74c52d0f,0x0f26449f,0xc28d362c,
        0x86f3311b,0x8adc4fb1,0xa4497e34,0xe0f0915d
    };
    int key[4];
    int len = sizeof(data) / sizeof(data[0]);
    for (int i = 0; i < 4; i++) {
        key[i] = "GEEK"[i];
    }

    for (int i = 0; i < len; i += 2) {
        int tmp[2] = { data[i], data[i + 1] };
        decrypt(tmp, key);
        data[i] = tmp[0];
        data[i + 1] = tmp[1];
    }

    for (int i = 0; i < len; i++) {
        printf("%c", (char)data[i]);
    }

    return 0;
}

也许你也听jay

简单题,主要是要先把变量名改一改,看得眼花

a2 = [0x96, 0xa1, 0xa0, 0x9b, 0x9b, 0x5f, 0x49, 0x46, 0x85, 0x82, 0x53, 0x95, 0x7d, 0x36, 0x8d, 0x74, 0x82, 0x88, 0x46, 0x7a, 0x81, 0x65, 0x80, 0x6c, 0x78, 0x2f, 0x6b, 0x6a, 0x27, 0x50, 0x61, 0x38, 0x3f, 0x37, 0x33, 0xf1, 0x27, 0x32, 0x34, 0x1f, 0x39, 0x23, 0xde, 0x1c, 0x17, 0xd4]
a3 = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D]
a4 = [0x5D, 0x5C, 0x5B, 0x5A, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]
a5 = [0x65, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5F, 0x5E, 0x5D, 0x5C, 0x5B, 0x5A, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x00, 0x31, 0x30, 0x2F]

for i in range(46):
    a5[i] += a3[i]
    a5[i] ^= a4[i+1]
for i in range(46):
    a2[i] -= a4[i]

for i in range(46):
    a3[i] ^= a5[51]
    a2[i] += a3[i+47]
for i in range(46):
    a2[i] ^= a3[i]

print(''.join([chr(i)for i in a2]))

解出来一个网址,进入网址后显示提示

加密方式为RC4,密文为Q7u+cyiOQtKHRMqZNzPpApgmTL4j+TE=秘钥为lovebeforeBC

直接用赛博厨子解密

注意输入格式选择base64

Hello_re

查壳,发现UPX壳,且无法工具脱壳,丢入010查特征码,发现UPX0被修改成SYC0,改回来后工具脱壳成功

ida反编译

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4[34]; // [rsp+20h] [rbp-60h] BYREF
  __int64 v5; // [rsp+A8h] [rbp+28h] BYREF
  char v6[32]; // [rsp+B0h] [rbp+30h] BYREF
  int v7[34]; // [rsp+D0h] [rbp+50h]
  int i; // [rsp+158h] [rbp+D8h]
  int v9; // [rsp+15Ch] [rbp+DCh]

  sub_14000173E(argc, argv, envp);
  v7[0] = 0;
  v7[1] = 1;
  v7[2] = 2;
  v7[3] = 52;
  v7[4] = 3;
  v7[5] = 96;
  v7[6] = 47;
  v7[7] = 28;
  v7[8] = 107;
  v7[9] = 15;
  v7[10] = 9;
  v7[11] = 24;
  v7[12] = 45;
  v7[13] = 62;
  v7[14] = 60;
  v7[15] = 2;
  v7[16] = 17;
  v7[17] = 123;
  v7[18] = 39;
  v7[19] = 58;
  v7[20] = 41;
  v7[21] = 48;
  v7[22] = 96;
  v7[23] = 26;
  v7[24] = 8;
  v7[25] = 52;
  v7[26] = 63;
  v7[27] = 100;
  v7[28] = 33;
  v7[29] = 106;
  v7[30] = 122;
  v7[31] = 48;
  v5 = 'REVOLCYS';
  sub_1400013B4("Please enter your flag:\n");
  sub_140001360("%32s", v6);
  sub_140001408(v6, &v5, v4);
  v9 = 1;
  for ( i = 0; i <= 31; ++i )
  {
    if ( v4[i] != v7[i] )
    {
      v9 = 0;
      break;
    }
  }
  if ( v9 )
    sub_1400013B4("Congratulations!\n");
  else
    sub_1400013B4("Try again!\n");
  return 0;
}

先动调取出v7的数据

0x00, 0x01, 0x02, 0x34, 0x03, 0x60, 0x2F, 0x1C, 0x6B, 0x0F, 0x09, 0x18, 0x2D, 0x3E, 0x3C, 0x02, 0x11, 0x7B, 0x27, 0x3A, 0x29, 0x30, 0x60, 0x1A, 0x08, 0x34, 0x3F, 0x64, 0x21, 0x6A, 0x7A, 0x30

注意到sub_7FF65AD71408函数为加密函数

void __stdcall sub_7FF65AD71408(char *a1, char *a2, int *a3)
{
  int j; // [rsp+8h] [rbp-8h]
  int i; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 31; ++i )
    a3[i] = a1[i];
  for ( j = 0; j <= 31; ++j )
    a3[j] ^= j ^ a2[j % 8];
}

简单加密,直接写脚本

a3 = [0x00, 0x01, 0x02, 0x34, 0x03, 0x60, 0x2F, 0x1C, 0x6B, 0x0F, 0x09, 0x18, 0x2D, 0x3E, 0x3C, 0x02, 0x11, 0x7B, 0x27, 0x3A, 0x29, 0x30, 0x60, 0x1A, 0x08, 0x34, 0x3F, 0x64, 0x21, 0x6A, 0x7A, 0x30]
a2 = list('REVOLCYS')
a2.reverse()
for i in range(len(a3)):
    a3[i] ^= i ^ ord(a2[i%8])
    print(chr(a3[i]),end='')

需要注意小端序的问题,ida反编译的时候会把字符串的顺序弄反,要手动逆一下顺序

我勒个z3啊

直接丢进ida反编译:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[8]; // [rsp+30h] [rbp-50h] BYREF
  __int64 v5; // [rsp+38h] [rbp-48h]
  int v6; // [rsp+40h] [rbp-40h]
  char v7[96]; // [rsp+50h] [rbp-30h] BYREF
  int v8; // [rsp+B0h] [rbp+30h]
  int i; // [rsp+BCh] [rbp+3Ch]

  sub_401F00(argc, argv, envp);
  memset(v7, 0, sizeof(v7));
  v8 = 0;
  *v4 = 0i64;
  v5 = 0i64;
  v6 = 0;
  sub_401C41();
  for ( i = 0; ; ++i )
  {
    byte_408030 = getchar();
    if ( byte_408030 == '\n' || i == 16 )
      break;
    v4[i] = byte_408030;
  }
  v4[i] = 0;
  sub_401B7B(v4); 
  puts("[+]>>>flag:");
  for ( i = 0; ; ++i )
  {
    byte_408030 = getchar();
    if ( byte_408030 == '\n' || i == 32 )
      break;
    v7[i] = byte_408030;
  }
  sub_401AAC(v7, v4);
  if ( !sub_40179A(v7) )
  {
    puts("[~]NO,Something_gets_wrong. TT.TT");
    exit(0);
  }
  puts("[~]Wow_you_get_it!!");
  return 0;
}

有两个函数sub_401B7B和sub_401AAC分别决定key和flag

sub_401B7B

int __fastcall sub_401B7B(const char *a1)
{
  char Destination[24]; // [rsp+20h] [rbp-20h] BYREF
  int j; // [rsp+38h] [rbp-8h]
  int i; // [rsp+3Ch] [rbp-4h]

  strncpy(Destination, a1, 17ui64);
  for ( i = 0; Destination[i]; ++i )
  {
    for ( j = 0; j <= 63; ++j )
    {
      if ( Destination[i] == a0123456789abcd[j] )
      {
        Destination[i] = j;
        break;
      }
    }
  }
  if ( strcmp(Destination, byte_404020) )
  {
    puts("[~]Maybe you should reverse this data creafully.see you again!(or idapatch is a good choise also)");
    exit(0);
  }
  return puts("[~]Good lets check your flag.");
}

根据a1在字符数组中的索引修改,存在byte_404020里

sub_401AAC为两个个简单加密

函数sub_40179A就需要用到z3来求解

这里取值使用lazyida插件能节省提取数据的麻烦,右键选择convert->(DWORD)即可

from z3 import *
enc = [0x19B, 0x113, 0x189, 0x1C9, 0x250, 0x536, 0x4DE, 0x1BC, 0x41B, 0x724, 0x6D0, 0x4A1, 0x645, 0x475, 0x4CA, 0x68C, 0x3E5, 0x1C7, 0x33D, 0x5B7, 0x28D, 0x244, 0x30E, 0x291, 0x271, 0x301, 0x45F, 0x46F, 0x517, 0x41E, 0x426, 0x4B5]
s = Solver()
a1 = Array('a1', IntSort(), IntSort())

for i in range(0, len(enc), 4):
    s.add(a1[i] + 8 * a1[i + 1] + 6 * a1[i + 2] + a1[i + 3] == enc[i])
    s.add(a1[i + 1] + 8 * a1[i + 2] + 6 * a1[i + 3] + a1[i] == enc[i + 1])
    s.add(a1[i + 2] + 8 * a1[i + 3] + 6 * a1[i] + a1[i + 1] == enc[i + 2])
    s.add(a1[i + 3] + 8 * a1[i] + 6 * a1[i + 1] + a1[i + 2] == enc[i + 3])

if s.check() == sat:
    model = s.model()
    for i in range(len(enc)):
        print(model.evaluate(a1[i]),end=',')
else:
    print("No solution")

攻击脚本:

zzz = [23,40,7,26,29,3,69,125,111,9,125,118,99,126,74,54,112,89,28,5,25,63,9,70,111,26,43,48,58,102,60,69]
abcs = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?_'
v4 = [0x2A, 0x0E, 0x0E, 0x14, 0x3F, 0x3F, 0x3F, 0x26, 0x11, 0x0A, 0x15, 0x15, 0x0E, 0x17, 0x10, 0x0E]
for i in range(len(v4)):
    v4[i] = ord(abcs[v4[i]])

v2 = len(zzz)
for i in range(31, -1, -1):
    zzz[i] ^= v4[(47 - i) % 16] ^ i
    zzz[i] ^= zzz[(v2 + i - 1) % v2]

for i in range(7, -1, -1):
    for j in range(i):
        v1 = zzz[4 * i + 3]
        for k in range(2, -1, -1):
            zzz[4 * i + k + 1] = zzz[4 * i + k]
        zzz[4 * i] = v1

print(''.join([chr(i)for i in zzz]))
0 条评论
某人
表情
可输入 255