Polar CTF Reverse简单 刷题笔记
看着大佬同学在写,我也得不甘示弱啊,刷就完了,不过后边那几道是真不简单啊
shell:
一上来就要脱壳,好家伙
不过,一脱完放进ida就可以看到flag

PE结构:
拖进DIE发现文件头不对

运行一下就行了

拼接:
文件头是zip改一下
32位,进入main函数,拼一块就行了

加加减减:
32位,进入main函数

一个简单的算法,将flag的每个字符的ASCLL码-1就是str2,逆向代码:
str1 = "ek`fz5123086/ce7ac7/`4a81`6/87b`b28a5|"
flag = " "
for i in range(len(str1)):flag += chr(ord(str1[i])-1)
print(flag)

康师傅:
32位,进入main函数

就是将str1和9异或即可
代码:
str1 = 'oehnr8>?;<?:9k>09;hj00o>:<o?8lh;8h9l;t'
flag = ''
for i in range(0,len(str1)):flag += chr(ord(str1[i]) ^ 9)
print(flag)
另辟蹊径:
运行程序,这是一个改值题,需要用Cheat Engine
这是需要运行进程中扫描

将其数值改为1,再运行

use_jadx_open_it:
apk文件用jadx打开,查找main函数

re2:
64位,进入main函数

代码就是将flag和输入的分别base64加密作比较,所以flag就是这个
layout:
apk文件,进入jadx,有个假的flag
用到雷电模拟器,打开

发现flag重合了,在开发助手用布局查看

Why32:
找一下算法,main函数里没找到啥线索,shift + F12查找字符串,看到敏感信息,跟进
先逆向算法
a = '2gfe8c8c4cde574f7:c6c;:;3;7;2gf:'
flag = ''
for i in range(0,len(a)):flag += chr(ord(a[i]) - 2)
print(flag)
但是提交了不对,看代码最后说对了一半,结合题目,还要MD5加密
?64:
64位,进入main函数,是个算法
让AI分析一下,这里详细解释一下这个v6数组,本来是_WORD[12],1个元素占2个字节,但是下面那个用了char指针,这时要按照字符串的存储读写方式,一个元素占一个字节,而接着下面的v6[8]=0,还是按照_WORD类型进行读取,所以这里截取相当于没有,还是全部加密
而这个加密方法简单,就是将字符串对应ASCLL值+1
a = 'YlwAY08ob1gkTlT<'
flag = ''
for i in range(0,len(a)):flag += chr(ord(a[i]) + 1)
print(flag)

其实以上步骤还可以简单通过运行这个程序获得,这是看了别人的博客知道的

一眼base64,解一下

但是提交不对,还得MD5加密

Sign Up:
64位,进入main函数,check就是算法
key_num-1,key_password-2,找到对应数据

key_num-1,key_password-2,找到对应数据

由此得到账号:081057009密码:pmmr
easyre1:
找到加密函数。但是具体数据时根据地址找到
str = "d^XSAozQPU^WOBU[VQOATZSE@AZZVOF"
str2 = "5055045045055045055045055045055"
flag = ""
for i in range(len(str2)):flag += chr((ord(str[i])+1)^ord(str2[i]))
print(flag)
babyRe:
类C语言,看到这样的很难受,进入main函数,endoce函数

可以让AI转C语言,大概思路就是将每个字符的ASCLL码+2就是flag,下一步就是找字符串,那就快捷键找字符串就行了

代码:
str = "asdfgcvbnmjgtlop"
flag = " "
for i in range(len(str)):flag += chr(ord(str[i]) + 2)
print (flag)
C^:
32位,进入main函数

fun1函数是加密函数

而check函数是验证函数

三者结合一下,就是v6[2]是flag存储的起始位置,也就是a1,而v8是flag的长度,也就是a2。加密算法就是将原始flag和1进行异或就是s
逆向代码:
str1 = "shfiu777"
flag = " "
for i in range(len(str1)):flag += chr(ord(str1[i])^1)
print(flag)

直接提交不对,还需要MD5加密
一个flag劈三瓣儿:
32位,进入main函数,三部分连起来即可

EasyCPP2:
64位,main函数里的encode加密函数很关键

查看flag

逆向代码:
cipher='qisngksofhuivvmg'
flag=''
for i in cipher:flag+=chr((ord(i)+3)^1)
print(flag)
crc:
64位,进入main函数,通过strmcpy函数从v11提取不同位置和长度的字符串到v10,进行magic函数处理后与目标字符串比较。进入magic函数发现是CRC32爆破
借用一下大佬的通用脚本用来对不同长度的字符串进行爆破:
import zlib
from itertools import product
def cacl_crc(s):'''计算crc'''crc=zlib.crc32(s.encode('utf-8'))&0xffffffff#有符号数字转为无符号return f'{crc:08x}'
# print(cacl_crc('text'))测试用例
# 分段规则:(起始索引, 长度, 目标CRC32)
def brute(target,length,chars):'''target:已知字符串chars:爆破字符集'''for combo in product(chars,repeat=length):candidate=''.join(combo)if cacl_crc(candidate) == target:print(f"找到原文,是{candidate}")return candidatereturn 0
def main():# 常用字符集(可根据题目调整,比如去掉大写字母)# 扩展字符集:包含大小写字母、数字、下划线、连字符、感叹号、问号、括号等常见符号chars = ("abcdefghijklmnopqrstuvwxyz" # 小写字母"ABCDEFGHIJKLMNOPQRSTUVWXYZ" # 大写字母"0123456789" # 数字"_-!@#$%^&*()[]{}<>?.,;:|=+" # 常见符号(根据CTF题目风格调整,去掉可能不相关的)
)result_segments = []segments = [(0, 4, "d1f4eb9a"), # 第1段:从input[0]开始,取4个字符(4, 1, "15d54739"), # 第2段:从input[4]开始,取1个字符(5, 4, "540bbb08"), # 第3段:从input[5]开始,取4个字符(9, 2, "3fcbd242"), # 第4段:从input[9]开始,取2个字符(11, 4, "2479c623"), # 第5段:从input[11]开始,取4个字符(15, 1, "fcb6e20c") # 第6段:从input[15]开始,取1个字符
]for i,(start,length,target) in enumerate(segments,1):print(f"开始爆破第{i}段:长度{length},目标CRC32={target}")segment = brute(target, length, chars)if not segment:return print("none")result_segments.append(segment)full_flag="".join(result_segments)print("结果是:"+full_flag)
if __name__ == "__main__":main()
爆破出来不加空格即可
box:
给了文本提示三段key拼到一块就是flag
64位,进入main函数分别分析

第一段key是一个算法

可以写代码:
key=0
c,d,f=0
for i in range(1,22):for j in range(1,2):c += id += cf = d + c - 1
for k in range(33,0,-1):c = f * d / 10key += f * d / 10
print(key)

第二段点开str1就是

第三段是base32加密,解码

解出来的拼到一块MD5加密
HowTo_Login:
需要先脱壳,UPX直接脱
32位,看到一个类似main函数的,点进去

看到Registion Success,跟登录有关,这个应该就是相关函数了
上面是登录邮箱格式验证,要求必须有@,有.,.后有字符,@后面不能紧跟.。那这就好办了,随便输入一个符合格式的就行
下面是一堆ASCLL码,分别转为字符,是CZ9dmq4c8g9G7bAX,这就是登录密码,再根据另一个文本提示,解出来的需要进行32位MD5加密,小写的

ezpack:
需要Aspack stripper脱壳工具https://link.gitcode.com/i/38d0a24f1582f6a288abc9124dbbd10a?uuid_tt_dd=10_37481620410-1752302325925-727688&isLogin=1&from_id=147330619
下载后拖进去就行了,找不到main函数,shift + 12找字符串,发现有Success等词语,跟进,交叉引用找到函数

在sub_sub_401648()函数里找到str2字符串

在sub_401378里找到算法

比较简单的异或,逆向代码即可:
str1 = ">4i44oo4?i=n>:m;8m4=oo4i;>?4>h9m"
flag = ""
for i in range(len(str1)):flag += chr(ord(str1[i]) ^ 12)
print (flag)
L00k_at_h3r3:
Unaspack脱壳,32位,进入main函数

整体是分别对每一段字符串进行加密,加密方式是一样的,都是分别和一个字符异或
那就分别分析写逆向代码
flag = " "
str1 = "lfkmqw"
for i in range(len(str1)):flag += chr(ord(str1[i]) ^ 0xa)
flag1 = " "
str2 = "nqT"
for i in range(len(str2)):flag += chr(ord(str2[i]) ^ 0xb)
flag2 = " "
str3 = "kixS"
for i in range(len(str3)):flag += chr(ord(str3[i]) ^ 0xc)
flag3 = " "
str4 = "ka9jR"
for i in range(len(str4)):flag += chr(ord(str4[i]) ^ 0xd)
flag4 = " "
str5 = "h|>cQ"
for i in range(len(str5)):flag += chr(ord(str5[i]) ^ 0xe)
flag5 = " "
str6 = "g<}<"
for i in range(len(str6)):flag += chr(ord(str6[i]) ^ 0xf)
print(flag+flag1+flag2+flag3+flag4+flag5)
解码器:
64位,进入main函数

重点是encyrpt这个算法

a1就是v5,a2就是v4,a3就是16,对v5的每个字符的ASCLL码加上对应索引i,再取模127,若结果<=31,那么就再加上32,输出v4
其实运行一下程序就有

这个就是v4,那么加密明文就是SELUTE TO LEGEND,提交不对,MD5即可

reserve_fib:
有一个文本,国庆假期放假三天。还有一个程序,64位,进入main函数

好长一段代码,慢慢来吧。前面是读取flag,去掉换行符。这是因为fgets函数输入时,回车后结尾会有换行符,此时字符串长度会多1,这就要替换为结束符就行;并且查看了是否越界。
将字符串转ASCLL码,然后就是输入一个整数,第39行的if语句是计算v7,根据题目给的另一个文本及反编译的算法,v7就是斐波那契数列的第v17项,也就是对第n项的斐波那契数列的计算,最后遍历输出
AI代码:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-def fib(n: int) -> int:"""按反编译逻辑计算第 n 个斐波那契数(n 从 1 开始,f(1)=1, f(2)=1)"""if n <= 0:return 0if n == 1 or n == 2:return 1a, b = 1, 1for _ in range(n - 2):a, b = b, a + breturn bdef transform_add(orig: str, k: int) -> str:"""对每个字符做 chr(ord(c) + k),并保证结果为单字节字符(取低 8 位)"""out = []for c in orig:out.append(chr((ord(c) + k) & 0xFF))return ''.join(out)def transform_sub(orig: str, k: int) -> str:"""对每个字符做 chr(ord(c) - k),并保证结果为单字节字符(取低 8 位)"""out = []for c in orig:out.append(chr((ord(c) - k) & 0xFF))return ''.join(out)def printable_view(s: str) -> str:return ''.join(ch if 32 <= ord(ch) < 127 else '.' for ch in s)def main():n = 3 # 题目给定orig = "um`svg_lehg_`_l" # 题目给定原始字符串,长度15k = fib(n)print(f"n = {n}, fib(n) = {k}")print("原始字符串:", orig)out_sub = transform_sub(orig, k)out_add = transform_add(orig, k)print("\n结果(按 Buffer[i] - fib(n)):")print("原始字节视图:", [ord(c) for c in orig])print("字符结果 :", out_sub)print("可打印视图 :", printable_view(out_sub))print("\n结果(按 Buffer[i] + fib(n)):")print("字符结果 :", out_add)print("可打印视图 :", printable_view(out_add))# 方便直接查看可能的 flag 格式print("\n包装为 flag{...} 的候选:")print("flag{", printable_view(out_sub), "}", sep="")print("flag{", printable_view(out_add), "}", sep="")if __name__ == '__main__':main()
答案就是wobuxiangjiaban,但是提交不对,MD5加密
re数独:
第一次碰到这种题,参考着大佬们的博客还有视频写。
一个程序合一个C语言,先看一下C语言是一个9宫格数独求解方法

会发现main这里少了原来数独的数据。还有一个程序,64位,main函数看看,这里额算法其实也是求解数独,那么现在就是要提取原本的数独了
方法一:在VS code里下断点,就是鼠标移到左边有个红点就可以下,一定下在solveSudoku()函数执行后,return函数执行前。右上角有调试,左边就出来了

方法二:AI写的代码直接解出来
#include <stdio.h>
#include <stdbool.h>#define N 9// 打印数独(优化格式,增强可读性)
void printGrid(int grid[N][N]) {for (int r = 0; r < N; r++) {// 每3行打印分隔线(适配3×3子网格)if (r % 3 == 0 && r != 0) {printf("-------------------------\n");}for (int d = 0; d < N; d++) {// 每3列打印分隔符if (d % 3 == 0 && d != 0) {printf(" | ");}printf("%d ", grid[r][d]);}printf("\n");}
}// 检查数字是否可填入当前位置
bool isSafe(int grid[N][N], int row, int col, int num) {for (int x = 0; x < N; x++) {// 检查行、列、3×3子网格是否存在重复数字if (grid[row][x] == num || grid[x][col] == num ||grid[row - row % 3 + x / 3][col - col % 3 + x % 3] == num) {return false;}}return true;
}// 数独求解回溯算法
bool solveSudoku(int grid[N][N]) {int row, col;bool isEmpty = true;// 查找空格(值为0的位置)for (row = 0; row < N; row++) {for (col = 0; col < N; col++) {if (grid[row][col] == 0) {isEmpty = false;break;}}if (!isEmpty) {break;}}// 无空格时说明数独已解if (isEmpty) {return true;}// 尝试填入1-9的数字for (int num = 1; num <= N; num++) {if (isSafe(grid, row, col, num)) {grid[row][col] = num;// 递归求解,若后续空格全部填满则返回成功if (solveSudoku(grid)) {return true;}// 回溯:当前数字无法导致解,恢复为空格grid[row][col] = 0;}}return false; // 无可行解时返回失败
}int main() {// 修复:移除数组最后一行的多余逗号(语法错误核心)int grid[N][N] = {{5, 3, 0, 0, 7, 0, 0, 0, 0},{6, 0, 0, 1, 9, 5, 0, 0, 0},{0, 9, 8, 0, 0, 0, 0, 6, 0},{8, 0, 0, 0, 6, 0, 0, 0, 3},{4, 0, 0, 8, 0, 3, 0, 0, 1},{7, 0, 0, 0, 2, 0, 0, 0, 6},{0, 6, 0, 0, 0, 0, 2, 8, 0},{0, 0, 0, 4, 1, 9, 0, 0, 5},{0, 0, 0, 0, 8, 0, 0, 7, 9} // 移除此处多余的逗号};printf("原始数独:\n");printGrid(grid); // 打印原始数独printf("\n求解结果:\n");if (solveSudoku(grid)) {printGrid(grid); // 求解成功,打印结果} else {printf("No solution exists");}return 0;
}
将最后一行MD5即可
易e:
32位,进入main函数,看不懂。这个程序太难了
看了解题视频,借助大佬某土的插件E-compiler,有个验证按钮

还是看不懂,问AI
主要算法就是将字符转ASCLL码+7,然后转回去为v27,再分别提取v27的高4位和低4位(还是原来长度),选取对应十六字符,拼一块
逆向代码:
# 一行完成解密,列表推导式比字符串拼接更高效
hex_str = "686D38373F6A68383C686B6B3737686A373C3F6C38383D3A406C38396C3A683D"
decrypted = ''.join([chr(ord(c) - 7) for c in bytes.fromhex(hex_str).decode()])
print("解密结果:", decrypted)

Polar_tasks:
64位,进入main函数,但是没找到什么有效信息,大都是初始化变量和运行空间,还有跟运行界面有关,且调用了一些函数。查找字符串,找到跟flag有关

需要计算地址,先查看dword_14001EA00=3
![]()
42*3=126,而一个数组占6个字节

126/6=21,也就是索引21
![]()
同理,aItsReverse
![]()
拼一块即可
tanchi:
打开发现是个贪吃蛇程序。有壳,用UnAspack脱,32位,进入main函数

这代码太长了,复制给AI看看。前面是控制台的相关操作还有用户输入的处理等
最重要的还是从字符串开始的代码

这是做了三轮字符变换。第一轮是将v28和0x23异或,结果为Arglist[0],将v28返回Arglist[1],接着同样的操作,知道碰到结束符
if ( !v28 )goto LABEL_60;v29 = ArgList;do{*v29++ = v28 ^ 0x23;v28 = *v29;}while ( *v29 );
第二轮,若字符为大写,那么v32 = (v30 - 44) % 26 + 65;若是小写,就v32 = (v30 - 76) % 26 + 97。相当于相当于把字母向右循环移动 +21(等于-5)
例如 'a'(97) → (97-76)=21 %26 =21 → +97 = 118 = 'v',确实 'a'->'v'
v30 = ArgList[0];
if ( !ArgList[0] ) goto LABEL_60;
do {if ( (unsigned __int8)(v30 - 97) > 0x19u ) {if ( (unsigned __int8)(v30 - 65) > 0x19u )goto LABEL_53;v32 = (v30 - 44) % 26 + 65;} else {v32 = (v30 - 76) % 26 + 97;}*v52 = v32;
LABEL_53:v33 = *++v31;...
} while ( v33 );
第三轮,对每个字符串c' = (c + 109) % 127,如果结果小于 32(不可打印控制字符),再 +32 以保证至少为可打印 ASCII(范围至少 32..126)
v34 = ArgList[0];
do {v36 = (v34 + 109) % 127;*v35 = v36;if ( v36 < 32 )*v35 = v36 + 32;... next char ...
} while ( v37 );
后面就是打印字符并且控制台显示与循环
那么可以根据这个加密算法写代码获得flag,AI的,Python还在尽力学习当中
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
穷举 v28 并模拟反编译得到的变换:
1) (可选)使用 v28 对原始字节做类似流式的 XOR(与反编译代码语义一致)
2) 对字母做 +21 的环形偏移(即小写:ch -> (ch-97+21)%26 + 97;大写同理)
3) 对每个字符做 (ord + 109) % 127 ,如果结果 < 32 则 +32 以保证可打印
我们穷举 v28 的取值 0..255,并且把 v28==0 视为跳过 XOR 的情况。
在所有结果中搜索包含 "flag{" 的输出并打印候选项。
"""
from typing import OptionalINIT = "`}{gvhBYnllosllYZsZsBkkAooslAiiiskAls"def emulate_transform(initial: str, v28: Optional[int]) -> str:# 步骤0:将字符串转为可变字符数组以便修改orig = list(initial) # 原始内容,用于 XOR 循环时作为“下一个字节”来源arr = list(initial) # 如果执行 XOR 步骤,将在 arr 上覆盖结果# 步骤1:反编译代码中的条件性流式 XOR 循环# if (!v28) 跳过该步骤if v28 and v28 != 0:# 按照推断实现语义:# 对 i=0..n-1:# arr[i] = cur ^ 0x23# cur = ord(orig[i+1]) if i+1 < len(orig) else 0# 如果 orig[i+1] == 0 则终止(对应 C 字符串以 NUL 结尾)n = len(orig)cur = v28 & 0xfffor i in range(n):arr[i] = chr((cur ^ 0x23) & 0xff)# 准备下一轮的 cur,从原始缓冲的下一个字节读取(如果存在)if i + 1 < n:next_byte = ord(orig[i+1])else:next_byte = 0cur = next_byte# 反编译循环在遇到下一个字节为 NUL 时结束,这里照做if next_byte == 0:break# 注意:如果原始字符串后面有未处理部分,按反编译语义这些位置保持原值# 否则跳过 XOR,arr 保持初始值# 步骤2:对字母进行 +21 的环形偏移(保持大小写)def rot_letter_plus21(ch: str) -> str:o = ord(ch)if 97 <= o <= 122:return chr(((o - 97 + 21) % 26) + 97)if 65 <= o <= 90:return chr(((o - 65 + 21) % 26) + 65)return charr = [rot_letter_plus21(c) for c in arr]# 步骤3:对每个字符做 (ord + 109) % 127,再保证结果 >= 32(可打印)out_chars = []for c in arr:v = (ord(c) + 109) % 127if v < 32:v = v + 32out_chars.append(chr(v))return ''.join(out_chars)def find_flag_candidates(initial):candidates = []# 先尝试 v28 == 0(跳过 XOR),用 0 表示该情况out0 = emulate_transform(initial, 0)candidates.append((0, out0))# 枚举 v28 从 1 到 255for v in range(1, 256):out = emulate_transform(initial, v)candidates.append((v, out))# 对结果按“有趣程度”进行过滤和排序interesting = []for v, s in candidates:lower = s.lower()score = 0# 如果包含 "flag{" 给一个很高的分数if "flag{" in lower:score += 1000# 否则以可打印字符数量作为启发式评分printable = sum(1 for ch in s if 32 <= ord(ch) < 127)score += printableinteresting.append((score, v, s))# 按分数从高到低排序interesting.sort(reverse=True)return interestingdef main():initial = INITres = find_flag_candidates(initial)# 打印前 30 个候选项print("Top 30 candidates (score, v28, output)...\n")for score, v, s in res[:30]:marker = " <-- contains 'flag{'" if "flag{" in s.lower() else ""print(f"v28={v:3d} score={score:4d} output: {s}{marker}")# 另外单独列出包含 flag{ 的所有候选print("\nAll candidates that include 'flag{' (case-insensitive):")found = Falsefor score, v, s in res:if "flag{" in s.lower():print(f"v28={v:3d} output: {s}")found = Trueif not found:print("None found. You can inspect top candidates above or try alternative interpretations.")if __name__ == "__main__":main()

