最近备战国赛刷刷题找手感,找了个还不错的靶场,小众但是题目质量还可以,下面是已经做的题的wp,不定时更新做题进度。
目录
简单难度
shell
先进行upx脱壳
放入IDA直接找到flag即可

PE结构
对文件头进行修复

IDA打开,找到字符串

反编译

写个简单的异或脚本
a = 'eobdx55:;4bgg30`:;b;e3`b`42f7`be`1b5b~'
flag = ''
for i in range(0,len(a)):
flag += chr(ord(a[i]) ^ 3)
print(flag)
或者直接运行exe文件

拼接
一个很简单那的字符串拼接
IDA打开就行

加加减减
IDA打开,就是对每个字符的ascll码减去数组下标

解密脚本
str = "ek`fz5123086/ce7ac7/`4a81`6/87b`b28a5|"
result = ''.join(chr(ord(c) + 1) for c in str)
print(result)
康师傅
IDA打开,一个简单的异或

解密脚本
a = 'oehnr8>?;<?:9k>09;hj00o>:<o?8lh;8h9l;t'
flag = ''
for i in range(0,len(a)):
flag += chr(ord(a[i]) ^ 9)
print(flag)
另辟蹊径
运行程序,是一个典型的改值题

使用Cheat Engine>选择进程>输入100000>扫描



双击改值
![]()

![]()
再去点击一下就行了

如果会动态调试的话可以用OD来做
打开后F9运行

句柄,刷新

其实这就是flag

use_jadx_open_it
直接用jadk打开
搜索main关键词

打开mainactivity,可以直接看到flag

re2
IDA打开,找到主函数点进去就看到了

Why32
IDA打开,找到主函数

写个脚本
a = '2gfe8c8c4cde574f7:c6c;:;3;7;2gf:'
flag = ''
for i in range(0,len(a)):
flag += chr(ord(a[i]) - 2)
print(flag)
运行结果是md5值,还需要进行md5解密
?64
尝试运行
![]()
base64解密,再对解密得到的内容进行md5加密

Sign Up
IDA打开,找到对登陆账号密码判断的函数

对账号的前七位-1,密码的四位-2
点开查看加密前的账号密码

由此得到账号:081057009密码:pmmr
flag{md5(账号密码)}
easyre1
不知道为什么反编译不完全,勉强做了一下

找到函数(主函数没反编译出来,看内存也可以看懂)


传入一个字符串
d^XSAozQPU^WOBU[VQOATZSE@AZZVOF
还有一个key

参考了其他师傅的题解,正常反编译出来就是前一个字符串按位-1和数字字符串进行异或
搓个脚本
result='d^XSAozQPU^WOBU[VQOATZSE@AZZVOF'
key = '5055045045055045055045055045055'
flag = ''
for i in range(len(key)):
flag += chr((ord(result[i])+1) ^ ord(key[i]))
print(flag)
flag{PolarDNbecomesbiggerandstronger}
babyRe
IDA打开
找main函数
查看encode函数

就是对flag进行按位+2操作
查看字符串找到明文

写脚本
a='asdfgcvbnmjgtlop'
flag = ''
for i in range(len(a)):
flag += chr((ord(a[i])+2))
print(flag)
flag{cufhiexdpolivnqr}
C^
IDA打开找到主函数

操作在funl中,和1进行异或

check函数中查看字符串

脚本
a='shfiu777'
flag = ''
for i in range(len(a)):
flag += chr((ord(a[i])) ^ 1)
print(flag)
flag{}要md5{right}
一个flag劈三瓣儿

EasyCPP2
IDA打开,找到字符串

跟到主函数

解密脚本
a='qisngksofhuivvmg'
flag = ''
for i in range(len(a)):
flag += chr((ord(a[i]) + 3) ^ 1)
print(flag)
crc
IDA打开找到主函数

打开strmncpy函数,是从输入的内容中进行字段选择的过程

打开magic函数,是CRC32计算

再回到main函数进行综合分析
这几个8位十六进制数其实就是从flag中取出1-4个字段进行crc计算后的结果
写了个通用的爆破脚本
import binascii
import string
print('-------------Start Crack CRC-------------')
def crack_crc_1(target):
comment = ''
chars = string.printable
for crc_value in target:
for char1 in chars:
char_crc = binascii.crc32(char1.encode()) # 获取遍历字符的CRC32值
calc_crc = char_crc & 0xffffffff # 将获取到的字符的CRC32值与0xffffffff进行与运算
if calc_crc == crc_value: # 将每个字符的CRC32值与每个文件的CRC32值进行匹配
comment += char1
print('{}'.format(comment))
def crack_crc_2(target):
comment = ''
chars = string.printable
for crc_value in target:
for char1 in chars:
for char2 in chars:
res_char = char1 + char2 # 获取遍历的任意2Byte字符
char_crc = binascii.crc32(res_char.encode()) # 获取遍历字符的CRC32值
calc_crc = char_crc & 0xffffffff # 将获取到的字符的CRC32值与0xffffffff进行与运算
if calc_crc == crc_value: # 将获取字符的CRC32值与每个文件的CRC32值进行匹配
comment += res_char
print('{}'.format(comment))
def crack_crc_3(target):
comment = ''
chars = string.printable
for crc_value in target:
for char1 in chars:
for char2 in chars:
for char3 in chars:
res_char = char1 + char2 + char3 # 获取遍历的任意3Byte字符
char_crc = binascii.crc32(res_char.encode()) # 获取遍历字符的CRC32值
calc_crc = char_crc & 0xffffffff # 将遍历的字符的CRC32值与0xffffffff进行与运算
if calc_crc == crc_value: # 将获取字符的CRC32值与每个文件的CRC32值进行匹配
comment += res_char
print('{}'.format(comment))
def crack_crc_4(target):
comment = ''
chars = string.printable
for crc_value in target:
for char1 in chars:
for char2 in chars:
for char3 in chars:
for char4 in chars:
res_char = char1 + char2 + char3 + char4 # 获取遍历的任意4Byte字符
char_crc = binascii.crc32(res_char.encode()) # 获取遍历字符的CRC32值
calc_crc = char_crc & 0xffffffff # 将遍历的字符的CRC32值与0xffffffff进行与运算
if calc_crc == crc_value: # 将获取字符的CRC32值与每个文件的CRC32值进行匹配
comment += res_char
print('{}'.format(comment))
if __name__ == '__main__':
crack_crc_4([0xd1f4eb9a])
crack_crc_1([0x15d54739])
crack_crc_4([0x540bbb08])
crack_crc_2([0x3fcbd242])
crack_crc_4([0x2479c623])
crack_crc_1([0xfcb6e20c])
print('-----------CRC Crack Completed-----------')
运行结果

box
IDA找到主函数

分别点开key1,key2,key3
一个比较简单的flag拼接题
flag1是一个简单的数字处理

脚本
c, d, f = 0, 0, 0
key = 0
for i in range(1, 22):
for j in range(1, 2):
c += i
d += c
f = d + c - 1
for k in range(33, 0, -1):
c = f * d // 10
key += f * d // 10
print(key)
key1:11694441
flag2直接点开就是了

key2:that_ok
flag3一段base32密文


flag{md5(11694441that_okkey)}
HowTo_Login
打开主函数

打开DialogFunc

密码就是CZ9dmq4c8g9G7bAX
flag{md5(CZ9dmq4c8g9G7bAX)}
中等难度
JunkCode
一道花指令的题
IDA打开后通过内存可以大概做出题目,但按照题目的本意应该是要进行花指令去除的

那么开始正规的解题过程

jz就是判断条件为1就跳转
但00411AC4为花指令,后面的内容无法被正常识别和解析,导致反编译失败
具体的操作方法是:选中内存地址00411AC4,按U键进行undefine

选中00411AC5处按C键MakeCode,再将00411AC4处的字节内容改为0x90



再选中00411AC5处按C键MakeCode
最后到函数头位置按P构建函数

再进行反编译

和刚开始看内存时的猜测一样,就是进行一次异或
解密脚本
a = 'dnceyhwli]amfg]kq]dwll{'
flag = ''
for i in range(0,len(a)):
flag += chr(ord(a[i]) ^ 2)
print(flag)
RevMethod
IDA打开有很多flag

IDA打开主函数 ,在随机生成flag

下面是对flag是否正确的判断过程
可以看出判断的是所找到的flag内存地址和预设的是否相同
并且通过每一位的比较,不断确定是否正确,显然每个flag的前五位都是一样的
所以不管找到哪个地址,前五位都是没有参考性的,但再往后两位就就可以确定flag了说明2566就是最后的flag所在的内存地址的位置41A000+A06

找到对应的内存位置
![]()
逆一下子
解法1
IDA打开,找到主函数

点进sub_401180

c68d-d262-5b91-2541-1ba7-1f3d-8103-41c4将中间的连接号去除就是flag
解法2
使用Resource Hacker打开

修改后点右上角的绿色三角进行编译,最后保存

再打开就可以点flag了


可以为师
可以发现帮助按钮点击不了
还是使用Resource Hacker打开

还是和上题一样
改完就可以打开了


flag{a5dd39834f606a4c00cc83d507c5e599}
左右为难
一个迷宫题
IDA打开发现迷宫

整理一下

flag{md5(sssssdddddwwaawwdddwddsssddwwddssdss)}
IDA打开找到主函数

Check_Length,判断输入的长度为37

主函数,创建了一个长度为3737的字符串

x()函数,每位分别与一个每次增长1的j进行异或

点开j看到初始值为8

check()函数,对密文`lfgc-y`b}v!进行检查

所以虽然有很多地方不明白具体的原因,但依旧可以正常的解题
写一个脚本
a = '`lfgc-y`b}v!'
result = ''
j = 8
for i in range(len(a)):
result += chr(ord(a[i]) ^ j) # 将字符的 ASCII 值减去索引后添加到 result
j = j + 1
print(result)
要注意的是,异或过程中,最后一位未进行
所以明文是hello world!
flag{md5(hello world!)}
Java_Tools
用jadx打开
main主函数
package main.java;
import java.util.Scanner;
/* loaded from: Test.class */
public class Test {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("Welcome to Polar_Ctf!,come to play!");
System.out.println("Please Input : ");
String name = in.next();
char[] Strings = name.toCharArray();
Tools.Add_1(Strings, 3);
Tools.Re(Strings);
Tools.Judge(Strings);
}
}
调用了三个函数
A_dd1、Re、Judge
打开Tool看看各个函数的功能
package main.java;
import java.util.ArrayList;
/* loaded from: Tools.class */
public class Tools {
public static int j = 6;
public static void Re(char[] str) {
for (int i = 0; i < (str.length / 2) - 1; i++) {
char temp = str[i];
str[i] = str[(str.length - i) - 1];
str[(str.length - i) - 1] = temp;
}
}
public static void Xor(char[] str) {
for (int i = 0; i < str.length; i++) {
str[i] = (char) (str[i] ^ j);
}
}
public static void Add_1(char[] str, int x) {
for (int i = 0; i < str.length; i++) {
str[i] = (char) (str[i] + x);
}
}
public static void Judge(char[] str) {
ArrayList<Character> Result = new ArrayList<>();
ArrayList<Character> Flag = new ArrayList<>();
for (char c : str) {
Character i = Character.valueOf(c);
Result.add(Character.valueOf(i.charValue()));
}
String sttr = new String(str);
if ("$gourZroohK".contains(sttr)) {
System.out.println("You Are Right!MD5!");
} else {
System.out.println("You Are Wrong! please try it again!");
}
char[] Strings = "$gourZroohK".toCharArray();
for (char c2 : Strings) {
Flag.add(Character.valueOf(c2));
}
if (Result.equals(Flag)) {
System.out.println("You Are Right!MD5!");
} else {
System.out.println("You Are Wrong! please try it again!");
}
}
}
由此可知,A_dd1是对字符串进行前后按位互换,Re对每一位进行了+3操作,Judge进行了字符串的比较。所以密文就是
$gourZroohK
手动换位就行
KhoorZruog$
每位-3
s = "KhoorZruog$"
s_list = list(s)
for i in range(len(s_list)):
s_list[i] = chr(ord(s_list[i]) - 3)
s = ''.join(s_list)
print(s)
flag{md5(HelloWorld!)}
PY_RE
下载两个py文件
Test.py
将后一半和字典数组进行比较,将字母换成对应的下标
def EnData1(Input_Str,Dict):
for i in range(int(len(Input_Str)/2),len(Input_Str)):
for dict in Dict:
if Input_Str[i] == str(dict):
Input_Str[i] = Dict[dict]
break
def Judge(Input_Str):
FLAG = ['H', 'E', 'L', 'L', 'O', '_', '_', 11, 2, 7, 19, 12, 13]
if str(Input_Str) == str(FLAG):
print("YES!")
else:
print("NO!")
all_data = []
def EnData(Input_Str,Dict):
for i in range(int(len(Input_Str)/2),len(Input_Str)):
flag = 0
for dict in Dict:
if Input_Str[i] == dict:
all_data.append(Dict[dict])
flag = 1
if flag == 0:
all_data.append(Input_Str[i])
start.py
生成字典的文件,其实可以直接看出字典的内容,也可以输出一下
#import Test
Dict = {}
key = 'A'
value = 26
for i in range(1,27):
Dict.setdefault(key, value)
key = chr(ord(key) + 1)
value = value - 1
print("===================Py_Reverse====================")
#print(Dict)
def main():
Input_Str = input("Please Input Str:\n")
Input_Str = list(Input_Str)
Test.EnData1(Input_Str,Dict)
Test.Judge(Input_Str)
main()

得到字典
{'A': 26, 'B': 25, 'C': 24, 'D': 23, 'E': 22, 'F': 21, 'G': 20, 'H': 19, 'I': 18, 'J': 17, 'K': 16, 'L': 15, 'M': 14, 'N': 13, 'O': 12, 'P': 11, 'Q': 10, 'R': 9, 'S': 8, 'T': 7, 'U': 6, 'V': 5, 'W': 4, 'X': 3, 'Y': 2, 'Z': 1}
将对应的数字换掉
HELLO__PYTHON
二层防御
UPX壳直接脱就行
IDA打开先看主函数

点开看看最后的check函数有没有什么操作


没什么操作,但是得到了flag1的值。那就直接看上一个sub122(x)

对str进行了一次前后互换
点开sub133看看

给出两个值v1=1;x1=j跟进一下看到j=8。同时将每一位-1之后和x1进行了异或
再看前的strlen和Check_Length()函数都未进行操作
所以逆向思路也很简单,每位+1,与8异或,最后进行反转就行
但有个小细节要注意,在进行反转和异或等操作时,都是从第二个字符到倒数第二个,也就是第一个和倒数第一个是不变的。
脚本随便写一个
flag1 = 'allo_PWN n'
for i in range(len(flag1)):
flag1 += chr((ord(flag1[i]) + 1) ^ 8)
print(flag1)
手动翻转一下就行
aeexhYPG)n——>a)GPYhxeen
最后md5就是flag(这一步在汇编里也有所体现,不再赘述)
350





