题目目录
- 1. 666
- 2.Reversing-x64Elf-100
- 3. easyRE1
- 4. insanity
- 5. open-source
- 6. game
- 7. Hello, CTF
- 8. re1
1. 666
下载附件
用IDA Pro打开文件,直接看到main入口,反编译查看代码如下:
注:strcmp(a,b)函数当a=b时返回值为0。
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[240]; // [rsp+0h] [rbp-1E0h] BYREF
char v5[240]; // [rsp+F0h] [rbp-F0h] BYREF
memset(s, 0, 0x1EuLL);
printf("Please Input Key: ");
__isoc99_scanf("%s", v5);
encode(v5, (__int64)s);
if ( strlen(v5) == key )
{
if ( !strcmp(s, enflag) )
puts("You are Right");
else
puts("flag{This_1s_f4cker_flag}");
}
return 0;
}
可以看到,输入的“Key”作为v5
字符串传入encode()函数中,然后将v5
的长度与key
比较,再比较字符串s
与enflag
,查看key和enflag的值:
我们点击进入函数encode()查看函数内容如下:
int __fastcall encode(const char *a1, __int64 a2)
{
char v3[104]; // [rsp+10h] [rbp-70h]
int v4; // [rsp+78h] [rbp-8h]
int i; // [rsp+7Ch] [rbp-4h]
i = 0;
v4 = 0;
if ( strlen(a1) != key )
return puts("Your Length is Wrong");
for ( i = 0; i < key; i += 3 )
{
v3[i + 64] = key ^ (a1[i] + 6);
v3[i + 33] = (a1[i + 1] - 6) ^ key;
v3[i + 2] = a1[i + 2] ^ 6 ^ key;
*(_BYTE *)(a2 + i) = v3[i + 64];
*(_BYTE *)(a2 + i + 1LL) = v3[i + 33];
*(_BYTE *)(a2 + i + 2LL) = v3[i + 2];
}
return a2;
}
可以看到第一段代码中的调用encode(v5,s),v5,s相当于下面encode函数代码中的a1,a2。
可以看到首先比较字符串a1的长度是否==key,然后将a1的每3个字节的值为一组,分别+
-
^
6,并和key进行异或操作,再将值赋值给a2对应位置的3个字节,最后返回a2的值。
因此encode(v5,s)就是将v5的值进行加密操作后得到s。
然后将s的值与enflag的值比较,相等即可。
因此我们写一个反向解密的脚本,将原来的加密操作反过来执行:
python:
#python脚本在将字符和整型进行操作时,没有办法直接转换,要自己进行字符类型转换
#s=enflag="izwhroz\"\"w\"v.K\".Ni"
s = ''
enflag = 'izwhroz""w"v.K".Ni'
key = 18
# 12h=16*1+2=18
for i in range(0, key, 3):
s += chr((ord(enflag[i]) ^ key) - 6)
s += chr((ord(enflag[i + 1]) ^ key) + 6)
s += chr((ord(enflag[i + 2]) ^ key) ^ 6)
print(s)
#unctf{b66_6b6_66b}
C:
#include <stdio.h>
int main(){
char s[20]={ };
char enflag[]= "izwhroz\"\"w\"v.K\".Ni";
int key=18;
for(int i=0;i<key;i+=3){
s[i] = (enflag[i] ^ key) - 6;
s[i + 1] = (enflag[i + 1] ^ key) + 6;
s[i + 2] = (enflag[i + 2] ^ key) ^ 6;
}
printf("%s",s);
}
//unctf{b66_6b6_66b}
2.Reversing-x64Elf-100
下载附件
找到入口函数main:
可以看到对输入的password用一个函数进行了操作:
我们进入这个函数查看,可以看到,需要函数返回0,即如下条件成立:
*(char *)(v3[i % 3] + 2 * (i / 3)) - *(char *)(i + a1) == 1
也就是 (a1+i)=(v3[i%3]+2*(i/3))-1
故脚本如下:
v = ["Dufhbmf", "pG`imos", "ewUglpt"]
a = ""
for i in range(12):
a += chr(ord(v[i % 3][2 * (i // 3)]) - 1)
print(a)
#输出:
#Code_Talkers
真的是被报错折磨半天,一直报错各种类型之类的问题,一开始一直以为是把v3[0]这种整个字符串变成int类型的值然后加上2*(i/3),后来看了解析终于发现,(char *)(v3[i % 3] + 2 * (i / 3))
,v3[i%3]只是一个地址,后面加上2*(i/3)是将指针后移,前面有个char*指针,也就是说v3[i % 3] + 2 * (i / 3)
是个地址。
可恶,太久没接触写代码,一直接触python脚本,已经忘了C语言了。
3. easyRE1
直接出现flag,给字符串加上flag{}。
4. insanity
进入查看strs数组:
5. open-source
源代码如下:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc != 4) {
printf("what?\n");
exit(1);
}
//需要4个参数,其中第一个参数为文件名
unsigned int first = atoi(argv[1]);
if (first != 0xcafe) {
printf("you are wrong, sorry.\n");
exit(2);
}
//第二个参数为0xcafe转整型51966
unsigned int second = atoi(argv[2]);
if (second % 5 == 3 || second % 17 != 8) {
printf("ha, you won't get it!\n");
exit(3);
}
//要求argv[2]%17==8且argv[2]%5!=3,则argv[2]取25
if (strcmp("h4cky0u", argv[3])) {
printf("so close, dude!\n");
exit(4);
}
//要求argv[3]="h4cky0u"
printf("Brr wrrr grr\n");
unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;
//计算hash=51966*31337+8*11+7-1615810207=12648430
printf("Get your key: ");
printf("%x\n", hash);
//转成16进制:c0ffee
return 0;
}
6. game
题目:
就和原神解谜一样让8行全部亮起来就行,本人就当作游戏纯手工试出来的(乖巧.jpg)
也可以IDA打开后改源码:
将判断条件后面5个都改成!=1,然后在控制台输入2,就可以获得flag。
7. Hello, CTF
main函数如下:
如图,首先将字符串"437261636b4d654a757374466f7246756e"
复制给v3,接下来v10被初始化为0。输入的内容作为v9,判断v9的长度>0x11则结束循环退出,因此要求输入内容长度<=0x11,也就是<=17。
然后接下来的for循环将v9的值赋值给v4,遇到空字符则退出,然后将v4的值以16进制形式写入Buffer中,再将Buffer接到v10后面,而由于v10初始化为全0,故V10的内容就是Buffer的内容。
随即比较v10与v13的值,相等则输出"Success"。也就是需要v10=“437261636b4d654a757374466f7246756e”,可以进行ASCII码转换得到flag:“CrackMeJustForFun”
8. re1
先打开文件逆向查看,前面又很多printf语句不知道打印了什么,最后的if语句输出两个字符串,不知道哪一个才是成功获得flag的反馈,于是直接运行exe程序查看如下:
因此猜测if语句的v3!=0时是flag错误的提示,也就是v3=0时,v7==v5.m128i_i8时获得flag,因此需要知道v5.m128i_i8的内容。
查看v5的结构如下:
_m128i是一个union联合类型,再看上面源码中,v5是将xmmword_413E34转换为_m128i类型的结构,因此我们取xmmword_413E34即为flag。
看到xmmword_413E34内容如下,数据类型为16进制,因此对其进行ASCII码转换得到flag。
要注意字节是从低到高位,转成ASCII码的排列顺序应为:
4455544354467B57653163306D657430
4455544354467D
一直以为源码中比较字符串时v5.m128i_i8只有16字节所以只比较16字节,没想到后面qword_413E44和前面xmmword是跟在后面一起的QAQ。没搞懂,还是说v5.m128i_i8只是一个地址,后面字符串全都算是一整个字符串。
看了别人的博客知道还可以用快捷键A或者右键把这串16进制直接转换成ASCII码: