这学期摸鱼的时候把[PolarCTF]平台的RE easy题做完了,遂发上来
(除了一个附件不太对的题)
Week-1
[NSSCTF][SWPUCTF 2021 新生赛]re1
exeinfo看一眼,64位。
拖进IDA打开,F5反编译
把里面的ascii都换成字符(按R),再理一下大概逻辑:
至于str2是什么,可以看到最开始str2和
{34sy_r3v3rs3}
进行了一次比较
逆向即:把{34sy_r3v3rs3}
中的3
换成e
,4
换成a
可得flag:NSSCTF{easy_reverse}
11.30
[ctfshow][逆向签到题]
直接IDA打开就能看见flag
[ctfshow][re2]
运行简单理解一下该程序:大概是用于加密文件的
IDA打开,shift+F12看看string,发现flag.txt。点进flag.txt,在ctrl+x看引用
找到主函数,F5(截图时,部分内容已被我重命名):重点是红框内的函数
sub_401069
:str xor 0x1F
后要 == "DH~mqqvqxB^||zll@Jq~jkwpmvez{"
=> 异或特性可知,str == "DH~mqqvqxB^||zll@Jq~jkwpmvez{" xor 0x1F
核心是sub_401028
这个函数,跟进去看看(截图时,部分内容已被我重命名)
挨个看吧
-
sub_4010F0
:用 key循环填充256位的数组
-
sub_4010C8
:s[i] = i
-
sub_4018E0
:用k_box对s_box进行置换
-
sub_4015E0
:rc4加密
这么理下来就很清楚了!
sub_4014E0
:将flagFile
文件里的内容,用rc4加密后放入enflagFile
里,key是DH~mqqvqxB^||zll@Jq~jkwpmvez{
与 0x1F 异或的结果
那么解密则需要:
- getKey:
DH~mqqvqxB^||zll@Jq~jkwpmvez{
与 0x1F 异或 - getFlag:key + enflag.txt =rc4=> flag.txt
/*
401A70: str xor 1f == DH~mqqvqxB^||zll@Jq~jkwpmvez{
*/
#include<stdio.h>
int main() {
char x[] = {"DH~mqqvqxB^||zll@Jq~jkwpmvez{"};
for (int i = 0; i < 30; i++) {
x[i] = x[i] ^ 0x1F;
printf("%c", x[i]);
}
//output: [Warnning]Access_Unauthorized
return 0;
}
rc4找在线解密即可:flag{RC4&->ENc0d3F1le}
12.2
[ctfshow][re3]
题目描述: 提交最小解即可,4位值
Die打开发现是64bit+linux
丢进IDA打开,shift+F12
看到字符串
Ctrl+x
跟进去,再F5
反编译
重命名+分析一下:
关键在于,我们通过阅读变量声明可以发现:因为输入时并未控制长度,因此我们可以对v19进行赋值
这个变换很复杂,但是都是固定数值没什么好分析的,因此我们直接转换成c语言,让电脑自己处理:
(我自己加了几个打印,方便查看)
#include<stdio.h>
int main() {
int v15 = 0;
unsigned __int64 v16 = 0LL;
int v17[7] = { 80, 64227, 226312059, -1540056586, 20496, 3833, 0 }; // 看看没赋值的情况
for (int i = 0; i <= 6; ++i) {
for (v16 += (unsigned int)v17[i]; v16 > 0xFFFF; v16 = v15 + (unsigned int)(unsigned __int16)v16) {
v15 = v16 >> 16;
}
printf("v16=%xn", v16);
printf("i=%dn", i);
}
printf("need = %xn",0xFFFF - v16);
if (v16 == 0xFFFF)
puts("OK");
else
puts("Error");
return 0;
}
因为最后效验需要v16 == 0xFFFF
,而v16+=v17[6] == 0xFFFF
时不会进入超级复杂的内层for
循环
所以我这里敢直接写0xFFFF - v16
将v17[6]
赋值为0x1a9f
确认一下:
12.16
[PolarCTF][ezpack]
发现是Aspack壳,用工具脱壳:
32位:用IDA打开。直接shift+f12发现关键字符“Enter password:”
直接跟进函数看看:
关键函数:sub_401738
。继续跟进去看看:
- 大概逻辑就是:
input
逐个和0xC
进行异或,然后和Str2
进行比较 - 所以
input = Str2 ^ 0xC
查看一下Str2 = >4i44oo4?i=n>:m;8m4=oo4i;>?4>h9m
编写解密脚本:
#include<stdio.h>
int main() {
char str[] = ">4i44oo4?i=n>:m;8m4=oo4i;>?4>h9m";
for (int i = 0; i < 32; i++) {
str[i] ^= 0xC;
}
printf("%s", str);
return 0;
}
// 28e88cc83e1b26a74a81cc8e72382d5a
[PolarCTF][L00k_at_h3r3]
查壳发现是NsPack,用工具脱壳:
32位,用IDA打开。直接搜索函数,找到main
函数:
很长,但实际上很简单,步骤大概是:
- 和几个数组逐个进行比较,且异或不同的数
- 例如第一个
for
循环,input[i]
和aNqt[i] ^ 0xBu
的结果进行比较 - 第二个
for
循环,input[i+len(aNqt)]
和aKixs[i] ^ 0xCu
的结果进行比较 - ...
- aNqt = nqT
- aKixs = kixS
- aKa9jr = ka9jR
- aHCq = h|>cQ
- aG = g<}<
编写解密脚本:
#include<stdio.h>
int main() {
char str1[] = "nqT";
char str2[] = "kixS";
char str3[] = "ka9jR";
char str4[] = "h|>cQ";
char str5[] = "g<}<";
for (int i = 0; i < sizeof(str1)-1; i++) {
str1[i] ^= 0xBu;
}
printf("%s", str1);
for (int i = 0; i < sizeof(str2) - 1; i++) {
str2[i] ^= 0xCu;
}
printf("%s", str2);
for (int i = 0; i < sizeof(str3) - 1; i++) {
str3[i] ^= 0xDu;
}
printf("%s", str3);
for (int i = 0; i < sizeof(str4) - 1; i++) {
str4[i] ^= 0xEu;
}
printf("%s", str4);
for (int i = 0; i < sizeof(str5) - 1; i++) {
str5[i] ^= 0xFu;
}
printf("%s", str5);
return 0;
}
//ez_get_fl4g_fr0m_h3r3
// 需要md5 32位小写加密后再提交
[ctfshow][re4]
DIE查看,发现是64位的
IDA64打开
12.18
[PolarCTF][shell]
查壳发现是upx
工具脱壳:upx -d [FilePath]
32位,IDA打开,shitf+F12
发现关键字符串。跟进去看看
F5反编译直接看到!
[PolarCTF][PE结构]
不是PE文件?根据题目,大概可以猜到,是想让我们修复PE结构。
winhex打开看看
上来就发现不是MZ!notepad++修改一下(别问为什么不是winhex,notepad用起来顺手一点QAQ)
再打开,发现OK了。
直接运行试试:
[PolarCTF][拼接]
32位,直接用IDA打开,发现脸上就是main函数,手直接就挪F5上了啊!
嗯······这拼接在,嗯。哈哈。
[PolarCTF][加加减减]
32位,IDA打开。main函数又在脸上,直接F5:
看眼逻辑:input
每位都--
,然后与str2
进行比较。
所以str2
每位都++
,就是flag
int main() {
char str2[] = "ekfz5123086/ce7ac7/
4a816/87b
b28a5|";
for (int i = 0; i < strlen(str2); i++) {
str2[i]++;
}
printf("%s", str2);
return 0;
}
[PolarCTF][HowTo_Login]
upx壳,工具脱掉:
32位,因为是注册机,所以先运行看看:
OK,上IDA,搜索字符串看看:
跟进去发现关键:
pawd会被打印,所以可以x64嗯调到打印的地方?
哦不行,试了一下发现,只会打印输入的东西,还是得自己对照QAQ
复制下来试试:
#include<stdio.h>
#include<string.h>
int main() {
char v11[20];
v11[0] = 'C';
v11[15] = 'X';
v11[1] = 'Z';
v11[14] = 'A';
v11[2] = '9';
v11[13] = 'b';
v11[3] = 'd';
v11[12] = '7';
v11[4] = 'm';
v11[11] = 'G';
v11[5] = 'q';
v11[10] = '9';
v11[6] = '4';
v11[9] = 'g';
v11[7] = 'c';
v11[8] = '8';
v11[16] = '�';
if (strlen(v11) == 0x10) {printf("%s", v11);}
return 0;
}
// CZ9dmq4c8g9G7bAX
运行检验一下:
哦对,邮箱要有@
,且@
后面有字符,有.
,且.
后面要有字符。反正写规范一点,直接123会被拦截
tip:最终密码要进行32位md5加密哦
丢到网页里32位小写md5一下即可:flag{c3ec13a01b07ad218dbd5f4bbab592b9}
12.19
[PolarCTF][box]
64位ELF。进kali看看:
程序入口是key2。IDA,启动!
跟进去发现,key2=str1
,str1 = that_ok = key2
接着找谁调用了key2()
,找到main
函数:
直接复制下来,运行一下发现:key1 = 11694441
key3 = NNSXS===
拼起来:flag{11694441that_okNNSXS===}
啊?不对。
草,key3需要base32解密,结果为:key
所以:flag{11694441that_okkey}
,再进行md5,32位小写加密即可
[PolarCTF][crc]
64位ELF,进kali看看:发现没有任何输出,直接等你输入。
好吧,那IDA启动!
简单分析一下strmncpy
函数:
简单分析一下magic
函数:
查找发现,python的binascii库和zlib库有crc32
因为flag的前4位包是flag,所以可以试试:flag
加密后是否和0xd1f4eb9a
相等,看看python库里的crc32的实现是否和这题的实现一致。
OK!可以开始爆破了
import binascii
for i in range(128):
str2 = chr(i)
crc2 = binascii.crc32(str2.encode())
if crc2 == 0x15d54739:
print("str2 = ", str2)
if crc2 == 0xfcb6e20c:
print("str6 = ", str2)
for j in range(128):
str4 = chr(i)+chr(j)
if binascii.crc32(str4.encode()) == 0x3fcbd242:
print("str4 = ", str4)
for k in range(128):
for l in range(128):
str1 = chr(i)+chr(j)+chr(k)+chr(l)
crc1 = binascii.crc32(str1.encode())
if crc1 == 0xd1f4eb9a:
print("str1 = ",str1)
if crc1 == 0x540bbb08:
print("str3 = ", str1)
if crc1 == 0x2479c623:
print("str5 = ", str1)
print("end")
拼起来:flag{ezrebyzhsh}
[PolarCTF][EasyCPP2]
64位ELF。直接运行发现:没有任何输出,直接等你输入。
IDA启动:
看了眼flag = qisngksofhuivvmg
再看眼encode()
:+=3
再^=1u
所以对flag = qisngksofhuivvmg
进行encode()
就是我们的input
#include<stdio.h>
#include<string.h>
int main() {
char flag[] = "qisngksofhuivvmg";
for (int i = 0; i <= 15; i++) {
flag[i] += 3;
flag[i] ^= 0x1u;
}
printf("%s", flag);
return 0;
}
//umwpkowshjymxxqk
[PolarCTF][一个flag劈三瓣儿]
64位ELF,直接运行:flag{HaiZI233N145wuD!le112@666}
啊?真这么简单wow!
[PolarCTF][C^]
64位ELF,直接运行:关键字符串“Please enter flag”
IDA启动!
fun1()
:a1[i] ^= 1u
check()
用于判断a1
是否和s
相等:s = shfiu777
所以flag = shfiu777 ^ 1u
#include<stdio.h>
int main() {
char flag[] = "shfiu777";
for (int i = 0; i <8; i++) {
flag[i] ^= 0x1u;
}
printf("%s", flag);
return 0;
}
//right666
再md5,32位小写加密即可
[PolarCTF][babyRE]
64位exe。运行以后发现,随便输入会输出“Err"
看眼endoce()
:对flag
的每位+2
那flag是啥嘞:shift+f12
里有一串诡异字符。可以试试
#include<stdio.h>
#include<string>
int main() {
char flag[] = "asdfgcvbnmjgtlop";
for (int i = 0; i <strlen(flag); i++) {
flag[i] +=2;
}
printf("%s", flag);
return 0;
}
//cufhiexdpolivnqr
[PolarCTF][easyre1]
64位ELF。运行发现:会输出”no no no"
IDA启动!
挨个看吧。
enkey()
:循环32次,134520896 + 96 = 134520992
看看这俩内容都是啥:
134520896: key = 5055045045055045055045055045055
134520992: flag
的位置
所以enkey()
就是让flag
和key
按位异或。
reduce()
:循环31次,flag
每位都-1
check()
:对比flag
和d^XSAozQPU^WOBU[VQOATZSE@AZZVOF
反过来就是:
-
d^XSAozQPU^WOBU[VQOATZSE@AZZVOF
每位都+1 - 然后和
key
按位异或
#include<stdio.h>
#include<string>
int main() {
char flag[] = "d^XSAozQPU^WOBU[VQOATZSE@AZZVOF";
char key[] = "5055045045055045055045055045055";
for (int i = 0; i <strlen(flag); i++) {
flag[i]++;
flag[i] ^= key[i];
}
printf("%s", flag);
return 0;
}
//PolarDNbecomesbiggerandstronger
[PolarCTF][Sign Up]
64位exe。直接运行发现关键字符串。
IDA启动!
非常eazy啊,key_num[i]-1 = name; key_password[i]-2 = password
key_num = 192168109; key_password = root
#include<stdio.h>
#include<string>
int main() {
char key_num[] = "192168109";
char key_password[] = "root";
for (int i = 0; i <strlen(key_num); i++) {
key_num[i]--;
}
for (int i = 0; i < strlen(key_password); i++) {
key_password[i] -= 2;
}
printf("name = %sn", key_num);
printf("password = %sn", key_password);
return 0;
}
//name = 0810570/8
//password = pmmr
//0810570/8pmmr
检验一下:
拼接起来md5即可。
等等,不对?
原来是眼睛不好使,没注意人家只替换了7个数
#include<stdio.h>
#include<string>
int main() {
char key_num[] = "192168109";
char key_password[] = "root";
for (int i = 0; i <=6; i++) {
key_num[i]--;
}
for (int i = 0; i <=3; i++) {
key_password[i] -= 2;
}
printf("name = %sn", key_num);
printf("password = %sn", key_password);
return 0;
}
//name = 081057009
//password = pmmr
这下对了!
[PolarCTF][?64]
64位可执行。
这,我猜是base64,直接在线解密试试:
赢!但是还是看看程序
遗憾!并不是base64的加密程序QAQ。收工
[PolarCTF][Why32]
64位exe
这几个函数连起来的意思就是:input.len == 32
继续看Do()
函数:input = cAry[i]-2; cAry = "2gfe8c8c4cde574f7:c6c;:;3;7;2gf:"
#include<stdio.h>
#include<string>
int main() {
char key_num[] = "2gfe8c8c4cde574f7:c6c;:;3;7;2gf:";
for (int i = 0; i <=31; i++) {
key_num[i]-=2;
}
printf("%sn", key_num);
return 0;
}
//0edc6a6a2abc352d58a4a98919590ed8
这个half right是什么意思呢?
不管了先直接包裹上flag提交试试,比较这个看起来很像md5加密之后的值。
豪德,不对。
解密试试呢?
F1laig
。这下对了!
12.20
[PolarCTF][康师傅]
32位exe。IDA直接跟进main
函数
input[i] ^= 9u == str1[i]
太简单了哇!直接str1[i] ^= 9u
就是flag了
#include<stdio.h>
#include<string>
int main() {
char flag[] = "oehnr8>?;<?:9k>09;hj00o>:<o?8lh;8h9l;t";
for (int i = 0; i < strlen(flag); i++) {
flag[i] ^= 9u;
}
printf("%sn", flag);
return 0;
}
//flag{17625630b7902ac99f735f61ea21a0e2}
[PolarCTF][re2]
64位ELF,IDA直接启动。
12.21
[NewStar CTF 2024][base64]
64位exe。IDA启动!
g84Gg6m2ATtVeYqUZ9xRnaBpBvOVZYtj+Tc=
关键函数是sub_1400014E0
,跟进去看看:
发现是BASE64的变形。只有索引表变了。
新的索引表在aWhydo3sthis7ab
里,具体是:WHydo3sThiS7ABLElO0k5trange+CZfVIGRvup81NKQbjmPzU4MDc9Y6q2XwFxJ/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define LOSU_BASE64_CODE_TABLE "WHydo3sThiS7ABLElO0k5trange+CZfVIGRvup81NKQbjmPzU4MDc9Y6q2XwFxJ/" //只有索引表改变,因此修改这里就行
#define LOSU_BASE64_CODE_PADDING (uint8_t)'='
int _decode(uint8_t x)
{
/*
该函数用于从编码表(LOSU_BASE64_CODE_TABLE)中查找对应字符的序号
在Base64Decode函数中,这大概率是性能制约部分
如需优化,可考虑使用二叉查找树进行优化
*/
for (int i = 0; i < strlen(LOSU_BASE64_CODE_TABLE); i++)
if (LOSU_BASE64_CODE_TABLE[i] == (char)x)
return i;
return -1;
}
uint8_t* Base64Decode(uint8_t* content, int length)
{
//创建缓冲区
int buffersize = (length / 4) * 3;
uint8_t* buffer = (uint8_t*)malloc(buffersize + sizeof(char));
memset(buffer, 0, buffersize + sizeof(char));
//计算填充字符个数
int left = 0;
if (content[length - 1] == LOSU_BASE64_CODE_PADDING){
left++;
} else if (content[length - 2] == LOSU_BASE64_CODE_PADDING) {
left++;
}
//解码
int i, j, i_max;
i_max = (left == 0 ? length : length - 4);
for (i = j = 0; i < i_max; i += 4, j += 3)
{
buffer[j] = ((_decode(content[i]) & 0x3f) << 2) + ((_decode(content[i + 1]) & 0x30) >> 4); //第一个byte的后6bit和第二个byte的第3到第4bit
buffer[j + 1] = ((_decode(content[i + 1]) & 0xf) << 4) + ((_decode(content[i + 2]) & 0x3c) >> 2); //第二个byte的第5到第8bit和第三个byte的第3到第6bit
buffer[j + 2] = ((_decode(content[i + 2]) & 0x3) << 6) + ((_decode(content[i + 3]) & 0x3f)); //第三个byte的第7到第8个bit和第四个byte的第3到第8bit
}
//处理填充的区块
switch (left)
{
case 0:
{
*((char*)&buffer[j]) = '�';
break;
}
case 1:
{
buffer[j] = ((_decode(content[i]) & 0x3f) << 2) + ((_decode(content[i + 1]) & 0x30) >> 4); //第一个byte的后6bit和第二个byte的第3到第4bit
buffer[j + 1] = ((_decode(content[i + 1]) & 0xf) << 4) + ((_decode(content[i + 2]) & 0x3c) >> 2); //第二个byte的第5到第8bit和第三个byte的第3到第6bit
*((char*)&buffer[j + 2]) = '�';
break;
}
case 2:
{
buffer[j] = ((_decode(content[i]) & 0x3f) << 2) + ((_decode(content[i + 1]) & 0x30) >> 4); //第一个byte的后6bit和第二个byte的第3到第4bit
buffer[j + 1] = ((_decode(content[i + 1]) & 0xf) << 4); //第二个byte的第5到第8bit
break;
}
}
return buffer;
}
int main() {
char cipher[] = "g84Gg6m2ATtVeYqUZ9xRnaBpBvOVZYtj+Tc=";
char* plain = (char*)Base64Decode((uint8_t*)cipher, strlen(cipher));
printf("%s",plain);
return 0;
}
// flag{y0u_kn0w_base64_well}
[PolarCTF][layout]
下载下来发现是apk。安装到雷电模拟器里看看。
疑似没做竖屏适配。调设置重启一下,发现还是乱码。
OK!上手段——jadx打开,直接搜索flag{
不对?!
好吧。回到雷电模拟器,用开发者助手提取:
这下对了
[PolarCTF][use_jadx_open_it]
这个名字——我听话,用 jadx 直接打开,然后搜索字符串:
结束
【未完成】[PolarCTF][另辟蹊径]
32位exe。但是注意:Section
是乱码。丢进虚拟机里运行发现果然运行不了。
拽进ida里发现会创建一个新文件,感觉不对。
然后搜了一下writeup,好像这个文件有问题。暂停解题
12.22
[PolarCTF][JunkCode]
32位可执行文件。IDA没看出名堂,进x64dbg试试。
根据题目名,猜测有很多无用代码,所以直接搜索字符串找到关键代码部分
简单分析(内容写注释里了):
分析认为junkcode.1F1258
是判断函数:
- 调用该函数后,有个jump,一个会输出funny(疑似成功?),一个会输出"no"。
- 运行测试时,发现输错 flag 就会输出"no"
下断点准备跟进去看,结果发现eax里就是flag~