【为美好CTF献上祝福】 New Star 2025 逆向笔记

Re

Week1

Strange Base

乍一看是个正常的 base64 加密

屏幕截图 2025-10-23 215142

点进 base64 函数一看,看着像个正常的 base64 加密

屏幕截图 2025-10-23 223610

原来是 aHello 这个数组的值跟正常 base64 的表不一样。

屏幕截图 2025-10-23 223915

只是单纯的 base64 换表

表是

HElLo!A=CrQzy-B4S3|is',27h,'waITt1ng&Y0u^{/(>v<)*}GO~256789pPqWXV

掏出我们的 cyberchef

屏幕截图 2025-10-23 230300

唉? 怎么解不出来啊 。

因为 y-B 会被 cyberchef 认为是从 y 到 B

需要在 - 前头加个 \

把表换成

HElLo!A=CrQzy\-B4S3|is'waITt1ng&Y0u^{/(>v<)*}GO~256789pPqWXVKJNMF

就行了。

屏幕截图 2025-10-23 230651

X0r

签到题,题目放 ida 后直接看到源码

点击查看代码
int __fastcall main(int argc, const char **argv, const char **envp)
{
  char Str2[32]; // [rsp+20h] [rbp-60h] BYREF
  _BYTE v5[16]; // [rsp+40h] [rbp-40h]
  char Str[36]; // [rsp+50h] [rbp-30h] BYREF
  int v7; // [rsp+74h] [rbp-Ch]
  int j; // [rsp+78h] [rbp-8h]
  int i; // [rsp+7Ch] [rbp-4h]

  _main();
  puts("Please input your flag: ");
  scanf("%25s", Str);
  v7 = strlen(Str);
  if ( v7 == 24 )
  {
    for ( i = 0; i < v7; ++i )
    {
      if ( i % 3 )
      {
        if ( i % 3 == 1 )
          Str[i] ^= 0x11u;
        else
          Str[i] ^= 0x45u;
      }
      else
      {
        Str[i] ^= 0x14u;
      }
    }
    v5[0] = 19;
    v5[1] = 19;
    v5[2] = 81;
    for ( j = 0; j < v7; ++j )
      Str[j] ^= v5[j % 3];
    strcpy(Str2, "anu`ym7wKLl$P]v3q%D]lHpi");
    if ( !strcmp(Str, Str2) )
      puts("Right flag!");
    else
      puts("Wrong flag!");
    return 0;
  }
  else
  {
    puts("Wrong flag length!");
    return 0;
  }
}

只是一个简单的异或加密,异或有个性质

A ^ B ^ B =A

写个脚本(我用的C++)求解 flag

点击查看代码
#include<bits/stdc++.h>
using namespace std;
char s[26]={"anu`ym7wKLl$P]v3q%D]lHpi"};  
int v5[3]; 
signed main(){
    for (int i=0;i<24;++i){
    	if ( i % 3 ){
            if ( i % 3 == 1 ) s[i] ^= 0x11u;
            else s[i] ^= 0x45u;
        }
      else s[i] ^= 0x14u;
	} 
    v5[0] = 19;
    v5[1] = 19;
    v5[2] = 81;	 
    for (int i=0;i<24;++i) {
    	s[i]^=v5[i%3];
		cout<<s[i]; 
	} 
	return 0;
} 

得到 flag 为

flag{y0u_Kn0W_b4s1C_xOr}

Puzzle

ida 使用教程题,把程序放进 ida 后按照提示一步一步走即可拼凑出 flag 。

flag 分四部分 。

先 shift + F12 拿到 flag2

屏幕截图 2025-10-23 234050

查一下字符串,找到 flag4

屏幕截图 2025-10-24 000121

无意间看到个可疑字符串,格式上应该是 flag1 (事实也确实是)

屏幕截图 2025-10-24 000608

接下来只剩下 flag3 了

看到左边列表有个函数名就叫 Its_about_part3()

点击查看代码
__int64 Its_about_part3()
{
  __int64 result; // rax
  _BYTE v1[10]; // [rsp+2Ah] [rbp-16h]
  int v2; // [rsp+34h] [rbp-Ch]
  int v3; // [rsp+38h] [rbp-8h]
  int i; // [rsp+3Ch] [rbp-4h]

  printf("You can use shift+e to extract the data.");
  v3 = 8;
  v2 = 8;
  for ( i = 0; i < v2; ++i )
    v1[i] = encrypted_array[i] ^ '\xFF\xFF\xFF\xAD';
  result = v2;
  v1[v2] = 0;
  return result;
}

将数据异或就能得到 flag3

把他们拼起来就能得到 flag 了

EzMyDroid

安卓逆向入门题,根据题目提示,掏出我们的 jadx

把下载下来的程序放进 jadx 后,先找到咱的 Main 函数(一开始直接搜索 flag 被诈骗了)

屏幕截图 2025-10-25 150916

点开 FirstFragment 得到如下代码:

点击查看代码
package work.pangbai.ezmydroid;

import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
import work.pangbai.ezmydroid.databinding.FragmentFirstBinding;

/* loaded from: classes2.dex */
public class FirstFragment extends Fragment {
    private FragmentFirstBinding binding;

    @Override // androidx.fragment.app.Fragment
    public View onCreateView(LayoutInflater layoutInflater, ViewGroup viewGroup, Bundle bundle) {
        FragmentFirstBinding fragmentFirstBindingInflate = FragmentFirstBinding.inflate(layoutInflater, viewGroup, false);
        this.binding = fragmentFirstBindingInflate;
        return fragmentFirstBindingInflate.getRoot();
    }

    @Override // androidx.fragment.app.Fragment
    public void onViewCreated(View view, Bundle bundle) {
        super.onViewCreated(view, bundle);
        this.binding.checkFlag.setOnClickListener(new View.OnClickListener() { // from class: work.pangbai.ezmydroid.FirstFragment.1
            @Override // android.view.View.OnClickListener
            public void onClick(View view2) {
                try {
                    String strEncrypt = AESECBUtils.encrypt(FirstFragment.this.binding.input.getText().toString(), "1145141919810000");
                    Log.i("result", strEncrypt);
                    if (strEncrypt.equals("cTz2pDhl8fRMfkkJXfqs2t8JBsqLkvQZDLYpWjEtkLE=")) {
                        Toast.makeText(FirstFragment.this.getContext(), "Right !!!", 0).show();
                    } else {
                        Toast.makeText(FirstFragment.this.getContext(), "Wrong !!!", 0).show();
                    }
                } catch (Exception unused) {
                }
            }
        });
    }

    @Override // androidx.fragment.app.Fragment
    public void onDestroyView() {
        super.onDestroyView();
        this.binding = null;
    }
}

再点开 AESECBUtils 函数,得到如下代码:

点击查看代码
package work.pangbai.ezmydroid;

import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

/* loaded from: classes2.dex */
public class AESECBUtils {
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";

    public static String encrypt(String str, String str2) throws Exception {
        SecretKeySpec secretKeySpec = new SecretKeySpec(str2.getBytes(), ALGORITHM);
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(1, secretKeySpec);
        return Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes("UTF-8")));
    }

    public static String decrypt(String str, String str2) throws Exception {
        SecretKeySpec secretKeySpec = new SecretKeySpec(str2.getBytes(), ALGORITHM);
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(2, secretKeySpec);
        return new String(cipher.doFinal(Base64.getDecoder().decode(str)), "UTF-8");
    }
}

不难发现是 AES 加密。ECB 模式 UTF-8

从 First 函数知道密钥为

1145141919810000

密文是

cTz2pDhl8fRMfkkJXfqs2t8JBsqLkvQZDLYpWjEtkLE=

直接 AES 发现出不来结果,仔细检查 AES 函数

屏幕截图 2025-10-25 151913

发现要先给密文 base64 一下 。

屏幕截图 2025-10-25 152337

得到flag

plzdebugme

这题需要在 linux 环境下动态调试。

推荐个博客 传送门

先把题目给出程序放到 ida 里,发现题目程序是个自解密程序

我们输入内容后,它会把密文进行自解密,再比对自解密后的文本与我们输入内容对比,判断 flag 对错。

既然如此,我们只要在解密完成后设个断点(不设断点运行结束数据是会清空的),调试时提取数据就能提取出 flag

把断点设在一切解密完成之后

屏幕截图 2025-10-25 163910

然后远程调试 ,对 x0r() 函数里的 flag 数组提取数据 ( shift + E )

得到 flag

屏幕截图 2025-10-25 165104

Week2

OhNativeEnc

这题提示我们 “安卓的 native 代码在哪呢” 。

我们把下载下来的文件解压后得到一个 .apk 文件,放入 jadx 找到主函数,再次看到提示

屏幕截图 2025-10-25 171959

我们对 .apk 文件进行二次解压 。

屏幕截图 2025-10-25 170553

一路找到这个 .so 文件

屏幕截图 2025-10-25 170632

掏出我们逆向万能的 ida ,打开这个 .so 文件 。

屏幕截图 2025-10-25 173008

打开 Java_work_pangbai_ohnativeenc_FirstFragment_checkFlag 这个函数,找到 native

得到如下代码

点击查看代码
char __fastcall Java_work_pangbai_ohnativeenc_FirstFragment_checkFlag(__int64 a1, __int64 a2, __int64 a3)
{
  const char *v3; // rbx
  unsigned int v4; // edi
  unsigned int v5; // r11d
  unsigned int v6; // r12d
  unsigned int v7; // edx
  unsigned int v8; // r14d
  unsigned int v9; // r9d
  unsigned int v10; // r10d
  unsigned int v11; // r13d
  unsigned int i; // r15d
  __int64 v13; // rax
  int v14; // r14d
  char v15; // al
  __int64 v16; // rdx
  unsigned __int64 v17; // rsi
  bool v18; // zf
  bool v19; // cf
  unsigned int v21; // [rsp+10h] [rbp-78h]
  char dest[16]; // [rsp+30h] [rbp-58h] BYREF
  __int128 v23; // [rsp+40h] [rbp-48h]
  unsigned __int64 v24; // [rsp+50h] [rbp-38h]

  v24 = __readfsqword(0x28u);
  v3 = (const char *)(*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1352LL))(a1, a3, 0);
  __android_log_print(4, "native", "input:%s", v3);
  v23 = 0;
  *(_OWORD *)dest = 0;
  strncpy(dest, v3, 0x20u);
  v4 = HIDWORD(v23);
  v5 = *(_DWORD *)dest;
  v6 = *(_DWORD *)&dest[4];
  v7 = *(_DWORD *)&dest[12];
  v8 = v23;
  v9 = DWORD1(v23);
  v10 = DWORD2(v23);
  v11 = *(_DWORD *)&dest[8];
  for ( i = 114514; i != 1488682; i += 114514 )
  {
    v21 = v8;
    v13 = (i >> 2) & 3;
    v14 = *(_DWORD *)&aThisisaxxteake[4 * v13];
    v5 += (((v4 >> 5) ^ (4 * v6)) + ((v6 >> 3) ^ (16 * v4))) ^ ((i ^ v6) + (v14 ^ v4));
    v6 += (((v5 >> 5) ^ (4 * v11)) + ((v11 >> 3) ^ (16 * v5)))
        ^ ((i ^ v11) + (v5 ^ *(_DWORD *)&aThisisaxxteake[4 * ((i >> 2) & 3 ^ 1)]));
    v11 += (((v6 >> 5) ^ (4 * v7)) + ((v7 >> 3) ^ (16 * v6)))
         ^ ((i ^ v7) + (v6 ^ *(_DWORD *)&aThisisaxxteake[4 * ((i >> 2) & 3 ^ 2)]));
    v7 += (((v11 >> 5) ^ (4 * v21)) + ((v21 >> 3) ^ (16 * v11)))
        ^ ((i ^ v21) + (v11 ^ *(_DWORD *)&aThisisaxxteake[4 * ((unsigned int)v13 ^ 3)]));
    v8 = v21 + ((((v7 >> 5) ^ (4 * v9)) + ((v9 >> 3) ^ (16 * v7))) ^ ((i ^ v9) + (v7 ^ v14)));
    v9 += (((v8 >> 5) ^ (4 * v10)) + ((v10 >> 3) ^ (16 * v8)))
        ^ ((i ^ v10) + (v8 ^ *(_DWORD *)&aThisisaxxteake[4 * ((i >> 2) & 3 ^ 1)]));
    v10 += (((v9 >> 5) ^ (4 * v4)) + ((v4 >> 3) ^ (16 * v9)))
         ^ ((i ^ v4) + (v9 ^ *(_DWORD *)&aThisisaxxteake[4 * ((i >> 2) & 3 ^ 2)]));
    v4 += (((v10 >> 5) ^ (4 * v5)) + ((v5 >> 3) ^ (16 * v10)))
        ^ ((i ^ v5) + (v10 ^ *(_DWORD *)&aThisisaxxteake[4 * ((unsigned int)v13 ^ 3)]));
  }
  *(_DWORD *)dest = v5;
  *(_DWORD *)&dest[4] = v6;
  *(_DWORD *)&dest[8] = v11;
  *(_DWORD *)&dest[12] = v7;
  *(_QWORD *)&v23 = __PAIR64__(v9, v8);
  *((_QWORD *)&v23 + 1) = __PAIR64__(v4, v10);
  v15 = 1;
  if ( (_BYTE)v5 == mm[0] )
  {
    v16 = -1;
    while ( 1 )
    {
      if ( dest[v16 + 2] != mm[v16 + 2] )
        return v15 ^ 1;
      if ( v16 == 29 )
        break;
      v17 = v16 + 2;
      v18 = dest[v16 + 3] == mm[v16 + 3];
      v16 += 2;
      if ( !v18 )
      {
        v19 = v17 < 0x1F;
LABEL_10:
        v15 = v19;
        return v15 ^ 1;
      }
    }
    v19 = 0;
    goto LABEL_10;
  }
  return v15 ^ 1;
}

接着我们对这串代码逆向。

(调这段逆向脚本调破防了)被学长提示这是 TEA加密

cyberchef 里没 TEA ,要么上网找工具,要么自己写脚本 >_<。

太菜了,拿AI写的 python 脚本。

点击查看代码
# xxtea_variant_decrypt_fixed.py
# 题目算法:8×u32 块,rounds=12,DELTA=114514,小端
# 关键修正:解密也要用 y = v[(p+1) % n]

DELTA = 114514

def to_u32s_le(b):  # bytes -> [u32...]
    assert len(b) % 4 == 0
    return [int.from_bytes(b[i:i+4], 'little') for i in range(0, len(b), 4)]

def from_u32s_le(v):  # [u32...] -> bytes
    return b''.join((x & 0xFFFFFFFF).to_bytes(4, 'little') for x in v)

def xxtea_decrypt(v, k):
    n = len(v)                     # = 8
    rounds = 6 + 52 // n           # = 12
    sum_ = (DELTA * rounds) & 0xFFFFFFFF
    v = v[:]                       # 不改原数组
    while sum_ != 0:
        e = (sum_ >> 2) & 3
        for p in range(n - 1, -1, -1):
            z = v[p - 1] if p > 0 else v[n - 1]
            y = v[(p + 1) % n]     # ★ 关键:用右邻
            mx = (((z >> 5) ^ ((y << 2) & 0xFFFFFFFF)) + ((y >> 3) ^ ((z << 4) & 0xFFFFFFFF))) \
                 ^ ((sum_ ^ y) + (k[(p & 3) ^ e] ^ z))
            v[p] = (v[p] - mx) & 0xFFFFFFFF
        sum_ = (sum_ - DELTA) & 0xFFFFFFFF
    return v

# 你的 mm(32B,按内存顺序)
mm_bytes = bytes([
    0xB6,0x53,0x6E,0x4D, 0x77,0x5D,0x08,0xD2, 0xFB,0x2C,0x63,0x1E,
    0xBB,0x7B,0x01,0x9B, 0xF5,0x04,0x6A,0xF4, 0x0E,0x84,0x27,0x47,
    0x64,0xA1,0xE4,0xD9, 0xEF,0x12,0x44,0x37
])

# 你的 key:字符串 “ThisIsAXXteaKey” 在 rodata 里是 ASCIIZ,所以用 16 字节:末尾补 \x00
key_bytes = b"ThisIsAXXteaKey\x00"

# 解密
v = to_u32s_le(mm_bytes)       # [v5,v6,v11,v7,v8,v9,v10,v4]
k = to_u32s_le(key_bytes)      # 4×u32
pt = from_u32s_le(xxtea_decrypt(v, k))

print("plaintext hex:", pt.hex())
print("as ASCII:", pt.rstrip(b'\x00').decode('utf-8'))

尤皮·埃克斯历险记(1)

题名是 UPX 的音译。

先拿 UPX 脱一下壳。

在 ida 里看到主要的加密部分

点击查看代码
int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax
  std::ostream *v4; // rax
  unsigned __int64 v5; // rax
  std::ostream *v6; // rax
  _BYTE v8[32]; // [rsp+20h] [rbp-80h] BYREF
  _BYTE v9[32]; // [rsp+40h] [rbp-60h] BYREF
  _QWORD v10[4]; // [rsp+60h] [rbp-40h] BYREF
  __int16 v11; // [rsp+80h] [rbp-20h]
  char v12; // [rsp+86h] [rbp-1Ah]
  char v13; // [rsp+87h] [rbp-19h]
  __int64 v14; // [rsp+88h] [rbp-18h]
  unsigned __int64 i; // [rsp+90h] [rbp-10h]
  char v16; // [rsp+9Fh] [rbp-1h]

  _main(argc, argv, envp);
  qmemcpy(v10, "isfhGJ\tt~cU\ny\nuTjcj\tT~cj", 24);
  v10[3] = 0x5047B777E756451LL;
  v11 = 16753;
  v14 = 34;
  std::string::basic_string(v9);
  std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Enter your flag: ");
  std::operator>><char>(refptr__ZSt3cin);
  encrypt(v8, v9); //划重点
  v3 = std::string::length(v8);
  if ( v14 == v3 )
  {
    v16 = 1;
    for ( i = 0; ; ++i )
    {
      v5 = std::string::length(v8);
      if ( i >= v5 )
        break;
      if ( IsDebuggerPresent() )
      {
        v12 = *(_BYTE *)std::string::operator[](v8, i) ^ 0xC3;
        if ( v12 != *((_BYTE *)v10 + i) )
        {
          v16 = 0;
          break;
        }
      }
      else
      {
        v13 = *(_BYTE *)std::string::operator[](v8, i) ^ 0x3C;
        if ( v13 != *((_BYTE *)v10 + i) )
        {
          v16 = 0;
          break;
        }
      }
    }
    if ( v16 )
      v6 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Right!");
    else
      v6 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Wrong!");
    refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(v6);
  }
  else
  {
    v4 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Wrong!");
    refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(v4);
  }
  std::string::~string(v8);
  std::string::~string(v9);
  return 0;
}
__int64 __fastcall encrypt(__int64 a1, __int64 a2)
{
  unsigned __int64 v2; // rax
  __int64 v4; // [rsp+0h] [rbp-40h] BYREF
  char v5; // [rsp+27h] [rbp-19h] BYREF
  char *v6; // [rsp+28h] [rbp-18h]
  char v7; // [rsp+37h] [rbp-9h]
  unsigned __int64 i; // [rsp+38h] [rbp-8h]

  v6 = (char *)&v4 + 39;
  std::string::basic_string<std::allocator<char>>(a1, &unk_1400C7000, v6);
  std::__new_allocator<char>::~__new_allocator(&v5);
  for ( i = 0; ; ++i )
  {
    v2 = std::string::length(a2);
    if ( i >= v2 )
      break;
    v7 = *(_BYTE *)std::string::operator[](a2, i);
    if ( (unsigned int)(v7 - 48) > 9 )
    {
      if ( islower(v7) || isupper(v7) ) //如果是字母,镜像转换并切换大小写。
        std::string::operator+=(a1, (unsigned int)(char)(-69 - v7)); 
      else
        std::string::operator+=(a1, (unsigned int)v7);
    }
    else
    {
      std::string::operator+=(a1, (unsigned int)(char)(105 - v7)); //数字做镜像转换
    }
  }
  return a1;
}

其中 encrypt 函数是对 字符串 a2 中的每个字节做如下变换,并把变换后的字节加到 a1 的后面:
如果该字节是数字就做 '0'->'9' , '1' -> '8' 的镜像转换

如果该字节是字母就做 'a'->'Z' , 'B' -> 'y' 的镜像并切换大小写转换。

解密脚本如下:

点击查看代码
#include <iostream>
#include <string>
#include <cctype>

static inline char inv_encrypt(unsigned char c) {
    // encrypt 的逆变换(与正变换相同,因其为自反映射)
    if (std::isdigit(c)) return static_cast<char>(105 - c);          // '0'..'9' 镜像
    if (std::islower(c) || std::isupper(c)) return static_cast<char>(187 - c); // 字母镜像并互换大小写
    return static_cast<char>(c);                                      // 其它原样
}

int main() {
    const unsigned char ct[] = {
        'i','s','f','h','G','J','\t','t','~','c','U','\n','y','\n','u','T','j','c','j','\t','T','~','c','j',
        'Q','d','u','~','w','{',0x04,0x05,'q','A'
    };
    const size_t n = sizeof(ct);

    std::string flag; flag.reserve(n);
    for (size_t i = 0; i < n; ++i) {
        unsigned char e = ct[i] ^ 0x3C;
        flag.push_back(inv_encrypt(e));
    }
    std::cout << flag << "\n";

    return 0;
}

Look at me carefully

这题就有点阴间了,一个考验脑洞的题。

放到 ida 后看到代码。

点击查看代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+0h] [ebp-7Ch]
  char v5; // [esp+0h] [ebp-7Ch]
  void *v6; // [esp+18h] [ebp-64h]
  char v7[40]; // [esp+1Ch] [ebp-60h] BYREF
  char v8[52]; // [esp+44h] [ebp-38h] BYREF

  strcpy(v7, "cH4_1elo{ookte?0dv_}alafle___5yygume");
  memset(v8, 0, 0x32u);
  v6 = calloc(0x25u, 1u);
  sub_401050(Format, v4);
  sub_4010C0(aS, (char)v8);
  if ( &v8[strlen(v8) + 1] - &v8[1] == 36 )
  {
    sub_4016E0(v6, v8, 27);
    sub_4016E0(v6, v8, 5);
    sub_4016E0(v6, v8, 6);
    sub_4016E0(v6, v8, 9);
    sub_4016E0(v6, v8, 28);
    sub_4016E0(v6, v8, 18);
    sub_4016E0(v6, v8, 32);
    sub_4016E0(v6, v8, 29);
    sub_4016E0(v6, v8, 4);
    sub_4016E0(v6, v8, 11);
    sub_4016E0(v6, v8, 15);
    sub_4016E0(v6, v8, 17);
    sub_4016E0(v6, v8, 22);
    sub_4016E0(v6, v8, 8);
    sub_4016E0(v6, v8, 34);
    sub_4016E0(v6, v8, 16);
    sub_4016E0(v6, v8, 19);
    sub_4016E0(v6, v8, 7);
    sub_4016E0(v6, v8, 26);
    sub_4016E0(v6, v8, 35);
    sub_4016E0(v6, v8, 2);
    sub_4016E0(v6, v8, 14);
    sub_4016E0(v6, v8, 21);
    sub_4016E0(v6, v8, 0);
    sub_4016E0(v6, v8, 1);
    sub_4016E0(v6, v8, 25);
    sub_4016E0(v6, v8, 13);
    sub_4016E0(v6, v8, 23);
    sub_4016E0(v6, v8, 20);
    sub_4016E0(v6, v8, 37);
    sub_4016E0(v6, v8, 30);
    sub_4016E0(v6, v8, 33);
    sub_4016E0(v6, v8, 10);
    sub_4016E0(v6, v8, 3);
    sub_4016E0(v6, v8, 12);
    sub_4016E0(v6, v8, 36);
    sub_4016E0(v6, v8, 24);
    sub_4016E0(v6, v8, 31);
    if ( sub_401880(v6, v7) )
    {
      sub_401050(aCongratulation, v5);
      return 0;
    }
    else
    {
      sub_401050(aTryAgain, v5);
      return -1;
    }
  }
  else
  {
    sub_401050(aTheFlagLengthI, v5);
    return -1;
  }
}

一个加密函数用了三十多次。打开加密函数后发现是加密套加密,看的头皮发麻。

题目提示我们不要仔细看这些加密函数里的东西。

我们仔细看看,加密后的字符串

cH4_1elo{ookte?0dv_}alafle___5yygume

长得特别像 flag 。

我们仔细观察下这个加密函数的格式

sub_4016E0( v6 , v8 ,一个 0-37 范围里的数字 )。

好巧不巧,加密函数执行了 38 次,且每个数字没有重复。换而言之,一串连续的数字都参与了加密。

cH4_1elo{ookte?0dv_}alafle___5yygume 的长度正好是 38 。

大胆猜测一波,这串字符串就算打乱顺序的 flag 。

c 在数组里是 a[27]
f 在数组里是 a[0]
l 在数组里是 a[1]

按照我们的猜测,重新给这串字符串排下序,得到 flag

Week3

谁改了我的密钥啊

太阴了这题。

安卓逆向题,题目提示 “你知道so加载会执行哪些函数吗” 。

如果单纯按照提示直接解压 .apk 后检查 so 文件,最后可能会在得到 flag 的前夜卡死。

先把 .apk 放到 jadx 里

源代码-> work.pangbai -> Main

找到整个题目非常关键的提示:

屏幕截图 2025-10-29 153458

“注意段序” 记住,要考。

然后我们按照提示,解压 .apk ,用 ida 去打开 so 文件。

开幕雷击,打开 so 文件后左边函数名提示了 SM4 加密。

屏幕截图 2025-10-29 153732

Java_work_pangbai_changemykey_FirstFragment_checkFlag 这个函数是检测 flag 的函数

打开看看。

屏幕截图 2025-10-29 154822

拿到 aKey 里的内容

屏幕截图 2025-10-29 160436

posted @ 2025-10-23 21:55  int_Hello_world  阅读(46)  评论(2)    收藏  举报