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文件解包:
- 将
pyinstxtractor-ng文件拖到main.exe同一目录下; - 命令提示符输入:
python pyinstxtractor-ng.py main.exe; - 得到文件夹:
main.exe_extracted。
进入文件夹,关注两个 pyc 文件:main 和 struct,以便于检查魔术字。
将两个程序分别拖入 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:

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表:

解密得: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,找到:

初步猜测这里对输入进行加密,然后输出。
验证猜想,右键核心函数 encrypt,点 分析,找到(类似IDA交叉引用):

点进 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!}

浙公网安备 33010602011771号