PCTF 2025 题解

Week 1

贪吃蛇

试玩发现,失败游戏会以弹窗形式提示信息。猜想,当达到一定分数,也会以弹窗形式提示flag。

所以重点放在寻找带有弹窗的函数。

找到 CGreedySnakeDlg 类,其中的 OnTimer 函数,发现有flag提示,写出脚本:

得到 flag: PCTF{Y0u_are_re11y_a_gam3_h4cker}

#include<bits/stdc++.h>
using namespace std;

int flag[100];

void init() {
             flag[0] = -81;
          flag[1] = -68;
          flag[2] = -85;
          flag[3] = -71;
          flag[4] = -124;
          flag[5] = -90;
          flag[6] = -49;
          flag[7] = -118;
          flag[8] = -96;
          flag[9] = -98;
          flag[10] = -115;
          flag[11] = -102;
          flag[12] = -96;
          flag[13] = -115;
          flag[14] = -102;
          flag[15] = -50;
          flag[16] = -50;
          flag[17] = -122;
          flag[18] = -96;
          flag[19] = -98;
          flag[20] = -96;
          flag[21] = -104;
          flag[22] = -98;
          flag[23] = -110;
          flag[24] = -52;
          flag[25] = -96;
          flag[26] = -105;
          flag[27] = -53;
          flag[28] = -100;
          flag[29] = -108;
          flag[30] = -102;
          flag[31] = -115;
          flag[32] = -126;
          flag[33] = 0;
}

int main() {
    init();
    for (int i = 0; i < 33; ++i ) flag[i] = ~flag[i];
    for(int i=0;i<33;i++) cout<<char(flag[i]);
}

Week 2

Maze

将程序拖进DIE,发现是python程序。(一开始不懂用了IDA,什么也没看出来,┭┮﹏┭┮)

考虑先将exe文件解包:

  1. pyinstxtractor-ng 文件拖到 main.exe 同一目录下;
  2. 命令提示符输入:python pyinstxtractor-ng.py main.exe
  3. 得到文件夹:main.exe_extracted

进入文件夹,关注两个 pyc 文件:mainstruct,以便于检查魔术字。

将两个程序分别拖入 010editor,发现 e3 之前的 16 位字相同。(看来是想多了...

然后用 uncompyle6 解码 main.pyc

uncompyle6 -o output main.pyc

先创建一个 output 文件夹,然后运行命令,就会在 output 下生成 main.py

打开 main.py

# uncompyle6 version 3.9.3
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]
# Embedded file name: main.py
import maze, hashlib
width = 61
height = 61
Maze = maze.get_map(width, height)
do_you_want_to_see_the_maze = False
print("Do you want to see the map?:", do_you_want_to_see_the_maze)
if do_you_want_to_see_the_maze:
    Maze.showMap()
path = input("Enter the shortest path to solve the maze (s: down, w: up, d: right, a: left): ")
x = 1
y = 1
for move in path:
    if move == "s":
        x += 1
    else:
        if move == "w":
            x -= 1
        else:
            if move == "d":
                y += 1
            else:
                if move == "a":
                    y -= 1
    if Maze.map[x][y] == 1:
        print("You hit a wall! Game over.")
        break
    if Maze.map[x][y] == 3:
        if len(path) > 116:
            print("You reached the exit but didn't take the shortest path! Game over.")
            break
        print("Congratulations! You've reached the exit!")
        path_md5 = hashlib.md5(path.encode("utf-8")).hexdigest()
        flag = "PCTF{" + path_md5 + "}"
        print("Here is your flag: " + flag)
        break

发现 do_you_want_to_see_the_maze = False,直接改成 True,然后运行代码,得到迷宫:

#############################################################
#S                      # #               # # #           # #
### # ### # # ####### # # # ### ##### ##### # # ### ### ### #
#   # #   # #       # #       #     #             #   #   # #
# # # # # # ##### ##### ####### # ### ######### # ######### #
# # # # # #     #     # # # # # #   # # # #   # #   #       #
# # # # # ##### ######### # # ### ##### # # ######### #######
# # # # #   # #   #           # #   #               # #     #
# # # # # # # ##### ####### # # ####### ############# # ### #
# # # # # #     #         # #         #                   # #
### # # ### ### # ################# ### ### ##### ###########
#   # #   #   #                 #   #     #   # #           #
# ##### # ### # ### ######### # ##### ##### # # #############
# #     #   # #   #         # # #         # #       #   #   #
### ### # ### ### # # ############# ### # ### ####### ### # #
#   #   #   #   # # #           #     # #   # # #         # #
# ### # # # ### # # # ########### # # # # ### # # ###########
# #   # # #   # # # #             # # # #   #           #   #
# ### ##### # # # # ### # ### ### # # ################### ###
# #   #     # # # # # # #   #   # # #           #           #
# ### ##### # ##### # ### ############# ####### # ######### #
# #     #   # #   #   #             # # #   # #           # #
# # # # # # ### # # # # # ##### ##### ### ### # ##### #######
# # # # # #     # # # # #     #               #     #       #
# ########### # # # ### # ####### ### ### # # ######### # ###
#   #         # # # #   #       #   #   # # #     # # # #   #
### ##### ### ##### ##### # # # # # ### # ### ##### # ##### #
#   #     #     #       # # # # # #   # #   #           #   #
# # # ##### # ##### # # # ### ### ### # # # # ############# #
# # # #     #     # # # #   # #     # # # # #             # #
# # # # ### # ##### ####### ### # ### # ### ####### ### # # #
# # # # #   #     #       #   # #   # #   #       #   # # # #
# # # # # # # ##### # ####### # # ### # # # # # # ####### ###
# # # # # # # #     #       # # #   # # # # # # #       #   #
# ##### ##### ### # ############### # ##### ########### # ###
# # #   #       # #             #   #     #           # #   #
# # # ### ### ### # ### ##### ### ### # # ### # # ###########
#   # #   #     # #   #     #   # #   # #   # # #           #
# # # ### # # ##### ##### # ### ### # ### # ##### # # # ### #
# # #   # # # # #       # #   #   # # #   #     # # # #   # #
### ##### # # # # ### # ####### ##### # ##### ########### ###
# # #     # # # # #   #       # #     #   #       #   #     #
# # # # # ### # ### # ####### # # # # # # ### ##### ##### # #
#   # # # #     #   #       # # # # # # # #             # # #
# ####### ##### ### # # ####### ### # # # ### ### ##### # ###
#   #         #   # # #       #   # # # # #     #     # #   #
# # ##### # # ### ### # ### ####### # # ####### ### # ##### #
# # #     # #   #   # #   #     # # # #     #     # #   # # #
# ##### ### # # # # ### # ####### # ### ####### ####### # ###
# #       # # # # #   # #         # #         #       #     #
####### # # # ### # # # # # ####### ### # ##### # ### # # # #
#       # # # # # # # # # #       #   # #   #   #   # # # # #
# ### # ### # # # ### ##### # ### # # # ### # # ##### ### # #
#   # # #   #   #   #   #   #   # # # #   # # # #   #   # # #
# ### # # ##### ####### # ####### ### # ##### ### # # ### ###
#   # # # #           # #     #     # #   #       # #   #   #
# # ### ### # # ####### # # ############### # ### ######### #
# #   # # # # #   #   # # #               # #   #     #   # #
##### ### ### ##### # # # ##### ### # ####### # # # ### ### #
#     #             # # #     #   # #     #   # # #       #E#
#############################################################

发现还要满足步数不超过116,直接运行脚本:

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
LL read() {
    char c=getchar(); LL sum=0,flag=1;
    while(c<'0'||c>'9') {if(c=='-') flag=-1; c=getchar();}
    while(c>='0'&&c<='9') {sum=sum*10+c-'0'; c=getchar();}
    return sum*flag;
}

const int N=70;
int st_x=1,st_y=1,ed_x=59,ed_y=59;
string binaryMap[61] = {
"1111111111111111111111111111111111111111111111111111111111111",
"1000000000000000000000001010000000000000001010100000000000101",
"1110101110101011111110101010111011111011111010101110111011101",
"1000101000101000000010100000001000001000000000000010001000101",
"1010101010101111101111101111111010111011111111101011111111101",
"1010101010100000100000101010101010001010101000101000100000001",
"1010101010111110111111111010101110111110101011111111101111111",
"1010101010001010001000000000001010001000000000000000101000001",
"1010101010101011111011111110101011111110111111111111101011101",
"1010101010100000100000000010100000000010000000000000000000101",
"1110101011101110101111111111111111101110111011111011111111111",
"1000101000100010000000000000000010001000001000101000000000001",
"1011111010111010111011111111101011111011111010101111111111111",
"1010000010001010001000000000101010000000001010000000100010001",
"1110111010111011101010111111111111101110101110111111101110101",
"1000100010001000101010000000000010000010100010101000000000101",
"1011101010101110101010111111111110101010101110101011111111111",
"1010001010100010101010000000000000101010100010000000000010001",
"1011101111101010101011101011101110101011111111111111111110111",
"1010001000001010101010101000100010101000000000001000000000001",
"1011101111101011111010111011111111111110111111101011111111101",
"1010000010001010001000100000000000001010100010100000000000101",
"1010101010101110101010101011111011111011101110101111101111111",
"1010101010100000101010101000001000000000000000100000100000001",
"1011111111111010101011101011111110111011101010111111111010111",
"1000100000000010101010001000000010001000101010000010101010001",
"1110111110111011111011111010101010101110101110111110101111101",
"1000100000100000100000001010101010100010100010000000000010001",
"1010101111101011111010101011101110111010101010111111111111101",
"1010101000001000001010101000101000001010101010000000000000101",
"1010101011101011111011111110111010111010111011111110111010101",
"1010101010001000001000000010001010001010001000000010001010101",
"1010101010101011111010111111101010111010101010101011111110111",
"1010101010101010000010000000101010001010101010101000000010001",
"1011111011111011101011111111111111101011111011111111111010111",
"1010100010000000101000000000000010001000001000000000001010001",
"1010101110111011101011101111101110111010101110101011111111111",
"1000101000100000101000100000100010100010100010101000000000001",
"1010101110101011111011111010111011101011101011111010101011101",
"1010100010101010100000001010001000101010001000001010101000101",
"1110111110101010101110101111111011111010111110111111111110111",
"1010100000101010101000100000001010000010001000000010001000001",
"1010101010111010111010111111101010101010101110111110111110101",
"1000101010100000100010000000101010101010101000000000000010101",
"1011111110111110111010101111111011101010101110111011111010111",
"1000100000000010001010100000001000101010101000001000001010001",
"1010111110101011101110101110111111101010111111101110101111101",
"1010100000101000100010100010000010101010000010000010100010101",
"1011111011101010101011101011111110101110111111101111111010111",
"1010000000101010101000101000000000101000000000100000001000001",
"1111111010101011101010101010111111101110101111101011101010101",
"1000000010101010101010101010000000100010100010001000101010101",
"1011101011101010101110111110101110101010111010101111101110101",
"1000101010001000100010001000100010101010001010101000100010101",
"1011101010111110111111101011111110111010111110111010101110111",
"1000101010100000000000101000001000001010001000000010100010001",
"1010111011101010111111101010111111111111111010111011111111101",
"1010001010101010001000101010000000000000001010001000001000101",
"1111101110111011111010101011111011101011111110101010111011101",
"1000001000000000000010101000001000101000001000101010000000101",
"1111111111111111111111111111111111111111111111111111111111111",

};

int flag;
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
char MP[4]={'d','a','s','w'};
int dui[4]={1,0,3,2};
int lst[N][N],vis[N][N];

void print(int x,int y) {
    string ans;
    while(x!=1||y!=1) {
        int pos=lst[x][y];
        ans+=MP[dui[pos]];
        x+=dx[pos]; y+=dy[pos];
    }
    reverse(ans.begin(),ans.end());
    cout<<ans;
}

void dfs(int x,int y,int step) {
    if(flag) return ;
    if(step>116) return ;
    if(x==ed_x&&y==ed_y) {
        print(x,y);
        flag=0;
        return ;
    }
    for(int i=0;i<4;i++) {
        int nx=x+dx[i],ny=y+dy[i];
        if(nx>=0&&nx<=60&&ny>=0&&ny<=60&&!vis[nx][ny]&&binaryMap[nx][ny]=='0') {
            vis[nx][ny]=1;
            lst[nx][ny]=dui[i];
            dfs(nx,ny,step+1);
            vis[nx][ny]=0;
        }
    }
}



int main(){
    vis[1][1]=1;
    dfs(1,1,0);
    return 0;
}
/*
ddddddddssssssddssddddssddddssddssssddddssssddddddssddssddssddssssssssddddssddssddssssddddssddddssddssddssssssddssss
*/

并将路径输入 main.exe,得到:

PCTF{9243bde4ab373794de19d751ca10e9b9}

ZeroTwo的权限

jadx-gui打开apk,前往主acticity,找到 ComposableSingletons$MainActivityKt,进入后找到 MainActivityKt,发现在 Greeting$lambda$9$lambda$8$lambda$7$lambda$6 函数中存在 “验证失败” 字符串,分析该函数。

public static final Unit Greeting$lambda$9$lambda$8$lambda$7$lambda$6(MutableState $inputState, MutableState $resultMessage, MutableState $resultColor) {
        String input = ((TextFieldValue) $inputState.getValue()).getText();
        int code = CryptoValidator.INSTANCE.v(input);
        switch (code) {
            case 1:
                $resultMessage.setValue(CryptoValidator.INSTANCE.F());
                $resultColor.setValue(Color.m4166boximpl(Color.INSTANCE.m4207getGreen0d7_KjU()));
                break;
            case 36:
            case MotionEventCompat.AXIS_GENERIC_6 /* 37 */:
            case MotionEventCompat.AXIS_GENERIC_7 /* 38 */:
            case MotionEventCompat.AXIS_GENERIC_8 /* 39 */:
            case MotionEventCompat.AXIS_GENERIC_11 /* 42 */:
            case 49:
            case AccessibilityNodeInfoCompat.MAX_NUMBER_OF_PREFETCHED_NODES /* 50 */:
            case 51:
            case 52:
                $resultMessage.setValue(CryptoValidator.INSTANCE.f(code, input));
                $resultColor.setValue(Color.m4166boximpl(Color.INSTANCE.m4214getYellow0d7_KjU()));
                break;
            default:
                $resultMessage.setValue("验证失败,请重试");
                $resultColor.setValue(Color.m4166boximpl(Color.INSTANCE.m4210getRed0d7_KjU()));
                break;
        }
        return Unit.INSTANCE;
    }

code=1 时,会输出绿色的提示信息,猜测为正确的回复。

进入 CryptoValidator.INSTANCE.v 函数,会来到 CryptoValidator 类中。

再找到 validate 函数,发现输入长度为 \(8\),且不含有子串 PCTF,所以此时猜测该apk通过输入一个提示信息,输出真正的flag。

观察代码逻辑,即将 input 加密和 R() 的结果比对。

不难用 java 写出如下解密程序:

// Source code is decompiled from a .class file using FernFlower decompiler (from Intellij IDEA).
public class test {
   public static int $stable;
   private static final String FC_HEX = "288EC0208CDB3A3CCC4BF6B5B2731E7E2B66096270417B26D9405A5607ACB471861DD07437A4F46F75600550";
   private static final int[] NP = new int[]{0, 12, 5, 8, 1, 10, 2, 15, 14, 3, 11, 4, 9, 7, 6, 13};
   private static final int[] NPinv;
   private static int gate;
   private static final byte[] k2025PCTF;
   private static final byte[] kNearMiss;
   private static final byte[] kPCTF2025;
   private static byte[] lastEnc;
   private static final int[] rcSeed;
   private static final int[] duiyin;
   private static final byte K = -1;
   private static final byte[] bArr;
   private static byte[] flag = new byte[8];
   private static byte[] id = new byte[8];

   static {
      int[] iArr = new int[16];

      for(int i = 0; i < 16; iArr[NP[i]] = i++) {
      }

      NPinv = iArr;
      rcSeed = new int[]{215, 31, 223, 184, 189, 157, 205, 105};
      k2025PCTF = new byte[]{-11, -12, -40, -109, -92, 68, -18, -115};
      kPCTF2025 = new byte[]{-98, -112, 55, 29, 107, 3, -14, -31};
      kNearMiss = new byte[]{61, 120, -85, 57, 51, 6, 101, -46};
      $stable = 8;
      int[] temp = new int[200];

      int i;
      for(i = 65; i <= 70; ++i) {
         temp[i] = i - 65 + 10;
      }

      for(i = 0; i <= 9; temp[i + 48] = i++) {
      }

      duiyin = temp;
      test obj = new test();
      bArr = obj.R();
   }

   public test() {
   }

   public byte[] R() {
      int length = rcSeed.length;
      byte[] tp = new byte[length];

      for(int i = 0; i < length; ++i) {
         tp[i] = (byte)(rcSeed[i] ^ i * 13 + 90 & 255);
      }

      return tp;
   }

   public static byte encode6(int i, byte num) {
      int v5 = (i + 119 & 255 ^ (num & -1) - i * i * 2 % 256 & 255) & 255;
      byte ans = (byte)((v5 << 4 | v5 >>> 4) & 255);
      return ans;
   }

   public static void decode() {
      for(int i = 0; i < 8; ++i) {
         for(byte j = -128; j <= 127; ++j) {
            if (encode6(i, j) == bArr[i]) {
               flag[i] = j;
               break;
            }

            if (j == 127) {
               break;
            }
         }
      }

   }

   public static byte[] encode4(byte x, byte y) {
      byte[] ans = new byte[2];
      int t = x & -1;
      int a = y & -1;
      int hiT = t >>> 4 & 15;
      int loT = t & 15;
      int hiA = a >>> 4 & 15;
      int loA = a & 15;
      int hiTp = NP[hiT];
      int loTp = NP[loT];
      int hiAp = NP[hiA];
      int loAp = NP[loA];
      ans[0] = (byte)(hiTp << 4 | loAp);
      ans[1] = (byte)(hiAp << 4 | loTp);
      return ans;
   }

   public static void decode2() {
      for(int i = 0; i < 4; ++i) {
         int ck = false;

         for(byte x = -128; x <= 127; ++x) {
            for(byte y = -128; y <= 127; ++y) {
               byte[] ans = encode4(x, y);
               if (ans[0] == flag[i] && ans[1] == flag[7 - i]) {
                  flag[i] = x;
                  flag[7 - i] = y;
                  ck = true;
                  break;
               }

               if (y == 127) {
                  break;
               }
            }

            if (x == 127 || ck) {
               break;
            }
         }

         System.out.println();
      }

   }

   public static byte encode(int i, byte num) {
      int v = num & -1;
      int k = i * 2 + 103 & 255;
      num = (byte)(v ^ k);
      int v2 = num & -1;
      num = (byte)((v2 << 2 | v2 >>> 6) & 255);
      int v3 = num & -1;
      num = (byte)((v3 - i * 5 + 11 & 255) * 5 - 23 & 255);
      int v4 = num & -1;
      int k1 = 187 - i & 255;
      int k2 = i * 3 + 68 & 255;
      int k3 = 136 - i * 2 & 255;
      int k4 = i * 4 + 221 & 255;
      num = (byte)(v4 ^ k1 ^ k2 ^ k3 ^ k4);
      return num;
   }

   public static void decode0() {
      for(int i = 0; i < 8; ++i) {
         for(byte j = 0; j <= 127; ++j) {
            if (encode(i, j) == flag[i]) {
               flag[i] = j;
               id[i] = 1;
               break;
            }

            if (j == 127) {
               break;
            }
         }
      }

   }

   public static void print() {
      for(int i = 0; i < 8; ++i) {
         System.out.printf("%d ", flag[i]);
      }

   }

   public static void main(String[] args) {
      decode();
      decode2();
      decode0();
      print();
      System.out.println();

      for(int i = 0; i < 8; ++i) {
         System.out.printf("%d ", id[i]);
      }

   }
}

得到提示信息:YnPcz3Zc

输入到apk中,即可得到 flag:

333

base64-pro

点进 main 函数,出现如下代码:

int __fastcall main(int argc, const char **argv, const char **envp)
{
  _BYTE v4[264]; // [rsp+0h] [rbp-110h] BYREF
  unsigned __int64 v5; // [rsp+108h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  printf("Enter the flag: ");
  __isoc99_scanf("%255s", v4);
  if ( (unsigned int)verify_flag(v4) )
    puts("Congratulations! Correct flag!");
  else
    puts("Wrong flag. Try again.");
  return 0;
}

继续点击 verify_flag

_BOOL8 __fastcall verify_flag(const char *s)
{
  size_t v1; // rax
  _BOOL4 v3; // [rsp+1Ch] [rbp-64h]
  __int64 v4; // [rsp+20h] [rbp-60h] BYREF
  char *s1; // [rsp+28h] [rbp-58h]
  char s2[72]; // [rsp+30h] [rbp-50h] BYREF
  unsigned __int64 v7; // [rsp+78h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  v1 = strlen(s);
  s1 = base64_encode((__int64)s, v1, &v4);
  strcpy(s2, "DIwDbmkDdswKrvsgYWSTrvkK4W5C6hS8Hf5NqzSTavDvwsizrfSg4qz=");
  v3 = strcmp(s1, s2) == 0;
  free(s1);
  return v3;
}

程序逻辑:对输入字符串s进行base64加密,结果为:DIwDbmkDdswKrvsgYWSTrvkK4W5C6hS8Hf5NqzSTavDvwsizrfSg4qz=

经过检查,base64_encode就是正常的base64加密,唯一有区别的是对应表不同。

点进base64_encode,发现对应表为:base64_chars

ctrl+x 交叉引用,发现该表在constructor() 函数被修改过。

发现修改函数复杂,直接使用linux远程调试,拿到base64表:

image-20251109094459121

解密得:PCTF{TLS_callback_destroyed_Base64_table}

flower_dance

DIE检测程序:

发现程序被 UPX 加密,但似乎是魔改版的 UPX,用 010editor 查看:

发现有:GEN0,SKT1,DWG! 三处与标准upx不同的地方(标准中是:UPX0,UPX1,UPX!)

将这三处都改成UPX,可以用UPX直接解包。

拖进IDA,发现有些.text段显示红色(IDA无法反编译),估计出现花指令。

发现:

.text:0040124E                 xor     ebx, ebx
.text:00401250                 test    ebx, ebx
.text:00401252                 jnz     short near ptr byte_401256
.text:00401254                 jz      short loc_401257

显然无论如何只会执行 jz short loc_401257,直接将 jnz short near ptr byte_401256 nop 掉。(注意有两处)

接着在函数头先按 u 再按 p 即可重新定义成函数。

根据字符串定位到 main 函数直接分析即可。

脚本:

/*
loc_87141F:对输入进行加密,然后和内存中固定的函数进行比较
loc_871220:输入加密函数


3Eh,3,28h,28h,81h,5Ah,0F9h,67h,8Bh,75h,4Ah,0A2h,7Dh,0E0h,0E8h,24h,8Fh,3Eh,0AAh,6Dh,0D1h,6Bh,35h,30h,0FFh,84h,5Ah,38h,75h,0CBh,84h,9,91h,27h,22h,0BBh,0FCh


37位字符串
*/

#include<bits/stdc++.h>
using namespace std;

typedef uint8_t _BYTE;
const int n17=17;
const int n0x25=37;
char v4; // [esp+0h] [ebp-7Ch]
char v5; // [esp+0h] [ebp-7Ch]
char This_is_a_rc4_key[20]; // [esp+3Ch] [ebp-40h] BYREF
_BYTE Buf2[40]={62, 3, 40, 40, 129, 90, 249, 103, 139, 117, 74, 162, 125, 224, 232, 36, 143, 62, 170, 109, 209, 107, 53, 48, 255, 132, 90, 56, 117, 203, 132, 9, 145, 39, 34, 187, 252}; // [esp+50h] [ebp-2Ch] BYREF
_BYTE v9[300]; // [esp+65h] [ebp-17h] BYREF
char p_Buf1[100];

void init() {
    strcpy(This_is_a_rc4_key, "This_is_a_rc4_key");
    // for(int i=0;i<17;i++) cout<<This_is_a_rc4_key[i]<<" ";
    // cout<<endl;
}

unsigned __int8 __cdecl sub_401100()
{
  unsigned __int8 n0x100_1; // al
  int v4; // [esp+0h] [ebp-114h]
  unsigned int i; // [esp+4h] [ebp-110h]
  int n256; // [esp+8h] [ebp-10Ch]
  unsigned __int8 n0x100_2; // [esp+Fh] [ebp-105h]
  _BYTE v8[256]; // [esp+10h] [ebp-104h] BYREF

//   n0x100_1 = (unsigned __int8)memset(v8, 0, sizeof(v8));
  for ( i = 0; i < 0x100; ++i )
  {
    n0x100_1 = i;
    v9[i] = i;
  }
  n256 = 0;
  v4 = 0;
  while ( n256 < 256 )
  {
    v4 = ((unsigned __int8)*(This_is_a_rc4_key + n256 % n17) + v4 + (unsigned __int8)v9[n256]) % 256;
    v4=(v4+256)%256;
    n0x100_2 = v9[n256];
    v9[n256] = v9[v4];
    n0x100_1 = n0x100_2;
    v9[v4] = n0x100_2;
    ++n256;
  }

//   for(int i=0;i<256;i++) cout<<int(v9[i])<<" ";

  return n0x100_1;
}

unsigned __int8 __cdecl sub_401220()
{
  unsigned __int8 result; // al
  unsigned int n0x25_1; // [esp+Ch] [ebp-110h]
  char v6; // [esp+15h] [ebp-107h]
  unsigned __int8 v7; // [esp+16h] [ebp-106h]
  unsigned __int8 v8; // [esp+17h] [ebp-105h]
//   _BYTE v9[256]; // [esp+18h] [ebp-104h] BYREF

  result = sub_401100();
  v8 = 0;
  v7 = 0;
  for ( n0x25_1 = 0; n0x25_1 < n0x25; ++n0x25_1 )
  {
    v7 += v9[++v8];
    v6 = v9[v8];
    v9[v8] = v9[v7];
    v9[v7] = v6;
    *(n0x25_1 + p_Buf1) = (Buf2[n0x25_1]^v9[(unsigned __int8)(v9[v7] + v9[v8])] ^ 0x56);
    result = n0x25_1 + 1;
  }
  return result;
}

int main() {
    init();
    scanf("%s",p_Buf1);
    int a=sub_401220();
    for(int i=0;i<37;i++) cout<<p_Buf1[i];
}
/*


*/

注意:此处v9为全局变量,本人便是直接复制忘记将 sub_401220 函数中的临时变量 v9 去掉,调了半天。。。

得到flag:PCTF{RC4_hid3_1n_fl0wers_4nd_ba9_UPX}

Week 3

encrypt_system

拖进DIE:

发现是C#和.Net架构,IDA无法分析,用 dnspy 进行分析。

将程序拖进 dnspy,找到:

image-20251117190108489

初步猜测这里对输入进行加密,然后输出。

验证猜想,右键核心函数 encrypt,点 分析,找到(类似IDA交叉引用):

image-20251117190310809

点进 Button_save_Click 函数,不难分析出程序逻辑确实如此。

分析加密函数,就是最常规的TEA加密程序,由于dnspy支持直接编辑,所以考虑直接将加密程序修改为解密程序。

注意:

byte[] array2 = Tea.genetateBytes(16);
byte[] array3 = Tea.genetateBytes(8);

这两句属于随机生成,不同的时间运行结果不一样,在写解密脚本时一定不要再调用这两句话生成array2和array3。发现:

Buffer.BlockCopy(array2, 0, array5, array.Length, 16);
Buffer.BlockCopy(array3, 0, array5, array.Length + 16, 8);

所以直接将加密文本的后面截下来给array2和array3即可。

右键 点 编辑类。进行修改,然后点 编译,再点 文件——全部保存。

下面是脚本(只对这两个函数做出更改):

		private static void EncryptBlock(ref uint v0, ref uint v1, uint[] key)
		{
			uint num = 0U;
			for (int i = 0; i < 65; i++)
			{
				num += 289739801U;
			}
			for (int j = 0; j < 65; j++)
			{
				v1 -= ((v0 << 4) + key[2] ^ v0 + num ^ (v0 >> 5) + key[3]);
				v0 -= ((v1 << 4) + key[0] ^ v1 + num ^ (v1 >> 5) + key[1]);
				num -= 289739801U;
			}
		}

		// Token: 0x0600000A RID: 10
		public static byte[] encrypt(byte[] inputBytes)
		{
			byte[] array = Tea.paddingBytes(inputBytes, 8);
			byte[] bytes = new byte[8];
			Tea.uint32ToBytes((uint)inputBytes.Length, bytes, 0);
			byte[] bytes2 = new byte[16];
			int len = inputBytes.Length;
			Buffer.BlockCopy(inputBytes, len - 24, bytes2, 0, 16);
			byte[] array2 = new byte[8];
			Buffer.BlockCopy(inputBytes, len - 8, array2, 0, 8);
			uint[] array3 = new uint[4];
			for (int i = 0; i < 4; i++)
			{
				array3[i] = Tea.bytesToUInt32(bytes2, i * 4);
			}
			byte[] array4 = new byte[array.Length - 24];
			for (int j = 0; j < array.Length - 24; j += 8)
			{
				byte[] bk = new byte[8];
				Buffer.BlockCopy(array, j, bk, 0, 8);
				uint nums = Tea.bytesToUInt32(array, j);
				uint nums2 = Tea.bytesToUInt32(array, j + 4);
				Tea.EncryptBlock(ref nums, ref nums2, array3);
				Tea.uint32ToBytes(nums, array, j);
				Tea.uint32ToBytes(nums2, array, j + 4);
				for (int k = 0; k < 8; k++)
				{
					byte[] array5 = array;
					int num4 = j + k;
					int num5 = num4;
					array5[num5] ^= array2[k];
				}
				Buffer.BlockCopy(bk, 0, array2, 0, 8);
				Buffer.BlockCopy(array, j, array4, j, 8);
			}
			return array4;
		}

然后再次运行程序,解密 flag.doc.encrypt ,得到:

喜欢我flag吗
PCTF{CSharp_encrypt_with_a_tea!}

posted @ 2026-01-15 11:48  2017BeiJiang  阅读(0)  评论(0)    收藏  举报