这个几天没有之前忙了,现在有时间整理一下之前做的攻防世界新手区的题目,记录一下学习过程,也复习一下
https://adworld.xctf.org.cn/
方法一:
下载附件,die查壳,无壳,
载入ida查看简单的明文比较,将比较过后的结果赋给v5,v5就是flag
查看上面两个字符串
一串数字按r转换为字符串如上,然后写出脚本即可得到flag
DUTCTF{We1c0met0DUTCTF}
str1='0tem0c1eW{FTCTUD'[::-1]
str2='}FTCTUD'[::-1]
print(str1+str2)
这里要注意数据大小端的储存方式,这里形成的字符串时逆序。
方法二:载入od中文搜索查看字符串,发现flag就在字符串里面,简单粗暴
下载附件
die查壳,无壳
方法一:
载入od动态调试,
注意记住上面的072E940是判断正确函数的入口点,然后现在的思路就很清晰了,就是找到判断我们输入是否正确的函数,将其跳转到判断正确的函数入口点来,也就是072E940地址。
接下来就是找输入的地方了,慢慢调试,找到%d
设置个断点,单步运行,找到跳转的指令将其改为jle long 0072e940,然后运行得到flag
方法二:
载入ida分析,shift+f12查看字符串,这样方便快速定位关键函数
这个函数就是关键函数,f5查看伪代码
函数有三部分构成
第一部分输出游戏的规则信息,第二部分就是游戏的运行部分,第三部分就是判断我们输入的n是否正确,
如果正确,然后跳到下面的sub_458054函数,猜测打印出flag,进入该函数看看
sub_45A7BE("done!!! the flag is ");
v59 = 18;
v60 = 64;
v61 = 98;
v62 = 5;
v63 = 2;
v64 = 4;
v65 = 6;
v66 = 3;
v67 = 6;
v68 = 48;
v69 = 49;
v70 = 65;
v71 = 32;
v72 = 12;
v73 = 48;
v74 = 65;
v75 = 31;
v76 = 78;
v77 = 62;
v78 = 32;
v79 = 49;
v80 = 32;
v81 = 1;
v82 = 57;
v83 = 96;
v84 = 3;
v85 = 21;
v86 = 9;
v87 = 4;
v88 = 62;
v89 = 3;
v90 = 5;
v91 = 4;
v92 = 1;
v93 = 2;
v94 = 3;
v95 = 44;
v96 = 65;
v97 = 78;
v98 = 32;
v99 = 16;
v100 = 97;
v101 = 54;
v102 = 16;
v103 = 44;
v104 = 52;
v105 = 32;
v106 = 64;
v107 = 89;
v108 = 45;
v109 = 32;
v110 = 65;
v111 = 15;
v112 = 34;
v113 = 18;
v114 = 16;
v115 = 0;
v2 = 123;
v3 = 32;
v4 = 18;
v5 = 98;
v6 = 119;
v7 = 108;
v8 = 65;
v9 = 41;
v10 = 124;
v11 = 80;
v12 = 125;
v13 = 38;
v14 = 124;
v15 = 111;
v16 = 74;
v17 = 49;
v18 = 83;
v19 = 108;
v20 = 94;
v21 = 108;
v22 = 84;
v23 = 6;
v24 = 96;
v25 = 83;
v26 = 44;
v27 = 121;
v28 = 104;
v29 = 110;
v30 = 32;
v31 = 95;
v32 = 117;
v33 = 101;
v34 = 99;
v35 = 123;
v36 = 127;
v37 = 119;
v38 = 96;
v39 = 48;
v40 = 107;
v41 = 71;
v42 = 92;
v43 = 29;
v44 = 81;
v45 = 107;
v46 = 90;
v47 = 85;
v48 = 64;
v49 = 12;
v50 = 43;
v51 = 76;
v52 = 86;
v53 = 13;
v54 = 114;
v55 = 1;
v56 = 117;
v57 = 126;
v58 = 0;
for ( i = 0; i < 56; ++i )
{
*(&v2 + i) ^= *(&v59 + i);
*(&v2 + i) ^= 0x13u;
}
return sub_45A7BE("%s\n");
}
果不其然,这就是打印flag的函数,py写出脚本,运行得到flag
zsctf{T9is_tOpic_1s_v5ry_int7resting_b6t_others_are_n0t}
a = [18, 64, 98, 5, 2, 4, 6, 3, 6, 48, 49, 65, 32, 12, 48, 65, 31, 78, 62, 32, 49, 32,
1, 57, 96, 3, 21, 9, 4, 62, 3, 5, 4, 1, 2, 3, 44, 65, 78, 32, 16, 97, 54, 16, 44,
52, 32, 64, 89, 45, 32, 65, 15, 34, 18, 16, 0]
b = [123, 32, 18, 98, 119, 108, 65, 41, 124, 80, 125, 38, 124, 111, 74, 49,
83, 108, 94, 108, 84, 6, 96, 83, 44, 121, 104, 110, 32, 95, 117, 101, 99,
123, 127, 119, 96, 48, 107, 71, 92, 29, 81, 107, 90, 85, 64, 12, 43, 76, 86,
13, 114, 1, 117, 126, 0]
str = ''
for i in range(0, 56):
b[0 + i] ^= a[0 + i]
b[0 + i] ^= 0x13
str = str + chr(b[i]);
print(str)
方法三,这是网上大佬的另外一种思路
if ( byte_532E28[0] == 1
&& byte_532E28[1] == 1
&& byte_532E28[2] == 1
&& byte_532E28[3] == 1
&& byte_532E28[4] == 1
&& byte_532E28[5] == 1
&& byte_532E28[6] == 1
&& byte_532E28[7] == 1 )
这里判断出我们需要将这八个数组的值都变为1,也就是8盏灯都闭合。
打开sub_4576D6函数
分析得到按键与电路闭合的关系:
按1--闭合1,2,8
按8--闭合1,7,8
按i(除1,8)--闭合i-1,i,i+1
附上大佬c+汇编爆破代码
#include
#include
using namespace std;
#define for(a,b,c) for(int a = b; a < c; ++a)
#define N 8
vector<int> flag(8,-1);
void func(int *arr){
for(i,0,N){
int n = arr[i];
if(n == 0){
flag[0] *= -1;
flag[1] *= -1;
flag[7] *= -1;
}else{
if(n == 7){
flag[0] *= -1;
flag[6] *= -1;
flag[7] *= -1;
}else{
flag[n] *= -1;
flag[n-1] *= -1;
flag[n+1] *= -1;
}
}
}
}
bool Judge(){
for(i,0,8)
if(flag[i] == -1)
return false;
return true;
}
int main(void)
{
int array[N] = {0};
for(i,0,8)
for(j,0,8)
for(k,0,8)
for(m,0,8)
for(n,0,8)
for(p,0,8)
for(q,0,8)
for(t,0,8){
array[0] = i;
array[1] = j;
array[2] = k;
array[3] = m;
array[4] = n;
array[5] = p;
array[6] = q;
array[7] = t;
func(array);
if(Judge()){
cout << "success:" << i+1 << j+1 << k+1 << m+1 << n+1 << p+1 << q+1 << t+1 << endl;
system("PAUSE");
}else{
for(x,0,8)
fill(flag.begin(), flag.end(), -1);
}
}
cout << "over!";
system("PAUSE");
return 0;
}
die查壳无壳,载入ida查看函数很简单,就是将我们输入的值进行16进制转换过后与v13进行比较,也就是说将v13的十六进制数转换位字符串就是flag
a = [0x43,0x72,0x61,0x63,0x6b,0x4d,0x65,0x4a,0x75,0x73,0x74,0x46,0x6f,0x72,0x46,0x75,0x6e]
flag = ""
for i in a:
flag += chr(i)
print(flag)
后记:这道题学习了sprintf这个函数,这个函数处理字符串可谓强大。
头文件:stdio.h
声明:int sprintf(char *string, char *format [,argument,…]);
功能:把格式化的数据写入某个字符串缓冲区。
例子1:
int main( void )
{
char buffer[200], s[] = "computer", c = 'l';
int i = 35, j;
float fp = 1.7320534f;
// 格式化并打印各种数据到buffer
j = sprintf( buffer, " String: %s\n", s ); // C4996
j += sprintf( buffer + j, " Character: %c\n", c ); // C4996
j += sprintf( buffer + j, " Integer: %d\n", i ); // C4996
j += sprintf( buffer + j, " Real: %f\n", fp );// C4996
printf( "Output:\n%s\ncharacter count = %d\n", buffer, j );
return 0;
}
>>>输出结果
>Output:
String: computer
Character: l
Integer: 35
Real: 1.732053
character count = 79
格式化数字字符串sprintf 最常见的应用之一莫过于把整数打印到字符串中
//把整数123 打印成一个字符串保存在s 中。
sprintf(s, "%d", 123); //产生"123"
sprintf(s, "%4d%4d", 123, 4567); //产生:" 1234567"
sprintf(s, "%-4d%4d", 123, 4567); //产生:"123 4567"
也可以按照16 进制打印:
sprintf(s, "%8x", 4567); //小写16 进制,宽度占8 个位置,右对齐
sprintf(s, "%-8X", 4568); //大写16 进制,宽度占8 个位置,左对齐
打印16 进制内容时,如果想要一种左边补0 的等宽格式,在表示宽度的数字前面加个0 就可以了。
sprintf(s, "%08X", 4567); //产生:"000011D7"
控制浮点数打印格式,默认保留小数点后6 位数字
sprintf(s, "%f", 3.1415926); //产生"3.141593"
但有时我们希望自己控制打印的宽度和小数位数,这时就应该使用:”%m.nf”格式,其中m 表示打印的宽度,n 表示小数点后的位数。比如:
sprintf(s, "%10.3f", 3.1415626); //产生:" 3.142"
sprintf(s, "%-10.3f", 3.1415626); //产生:"3.142 "
sprintf(s, "%.3f", 3.1415626); //不指定总宽度,产生:"3.142"
连接字符串
char buf[60]={0};
char*who="I";
char*whom="CSDN";
sprintf(buf,"%slove%s.",who,whom);
printf("%s",buf);
//输出结果:"IloveCSDN."
下载附件,附件是一份c语言源码,这就很简单了
1 #include <stdio.h>
2 #include <string.h>
3
4 int main(int argc, char *argv[]) {
5 if (argc != 4) {
6 printf("what?\n");
7 exit(1);
8 }
9
10 unsigned int first = atoi(argv[1]);
11 if (first != 0xcafe) {
12 printf("you are wrong, sorry.\n");
13 exit(2);
14 }
15
16 unsigned int second = atoi(argv[2]);
17 if (second % 5 == 3 || second % 17 != 8) {
18 printf("ha, you won't get it!\n");
19 exit(3);
20 }
21
22 if (strcmp("h4cky0u", argv[3])) {
23 printf("so close, dude!\n");
24 exit(4);
25 }
26
27 printf("Brr wrrr grr\n");
28
29 unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;
30
31 printf("Get your key: ");
32 printf("%x\n", hash);
33
34 return 0;
35 }
第29行计算flag,第32行代码输出十六进制形式
三部分组成,三个数组运算
第一部分:
if (first != 0xcafe) {
printf("you are wrong, sorry.\n");
exit(2);
}
first=0xcafe
第二部分:
if (second % 5 == 3 || second % 17 != 8) {
printf("ha, you won't get it!\n");
exit(3);
}
second=25
第三部分:
if (strcmp("h4cky0u", argv[3])) {
printf("so close, dude!\n");
exit(4);
}
相等strcmp返回0,退出if条件,那argv[3]=“h4cky0u”
综上,用C语言写出脚本
#include
#include
int main(int argc, char* argv[])
{
int first = 0xcafe;
int second = 25;
argv[3] = "h4cky0u";
unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;
printf("%x\n", hash);
return 0;
}
下载附件elf文件,die查壳,64位,upx的壳,linux下upx -d 脱壳
如何脱壳:网上去下载upx-3.95-amd64_linux.tar.xz,解压缩到linux一个文件夹,然后将要脱壳的文件也扔进这个文件夹,然后就再该文件夹下打开终端,再./upx -d “要脱壳的文件名”(注意没有“”哈)!
载入ida64,找到main函数,就出现的这个flag就是真的flag
die查壳,无壳,载入ida
这里要注意大小端序的问题,接下来写出py脚本
v7 = 'ebmarah'[::-1]
v8 = ":\"AL_RT^L*.?+6/46"
for i in range(len(v8)):
print(chr(ord(v7[i % 7]) ^ ord(v8[i])),end='')
遗留问题:什么时候该大小端序转换?
查壳无壳32位,载入ida,shift+f12查找字符串,直接出来flag,当真如题目所说,来个简单的签到题。。。
查壳,无壳,32位
这是关键函数
处理部分在这个加密函数decrypt里面
idapy脚本将传入decrypt() 的两个参数打印出来,注意舍去最后的0
addr1=0x8048a90
addr2=0x8048aa8
a2=[]
dest=[]
for i in range(6):
a2.append(Dword(addr1+4*i))
for j in range(38):
dest.append(Dword(addr2+4*j))
print(a2)
print(dest)
a2 = [5121, 5122, 5123, 5124, 5125]
dest = [5178, 5174, 5175, 5179, 5248, 5242, 5233, 5240, 5219, 5222, 5235, 5223, 5218, 5221, 5235, 5216, 5227, 5233, 5240, 5226, 5235, 5232, 5220, 5240, 5230, 5232, 5232, 5220, 5232, 5220, 5230, 5243, 5238, 5240, 5226, 5235, 5243, 5248]
i = j = 0
while i < 38:
j = 0
while j <5 and i < 38:
dest[i] -= a2[j]
i += 1
j += 1
flag = ''
for i in range(38):
flag+= (chr(dest[i]))
print(flag)
查壳无壳,载入ida
发现就两个函数,一个调用窗口的messageboxa函数一个判断是否被调试的函数,推断不乱码的函数是sub_401000函数,这里面有真正的flag
点进去看,一个加密函数,但是上面的流程图显示,这个函数应该没有被执行,
现在载入od看看,中文搜索字符串flag,找到关键地点,
果不其然,sub_401000函数被跳过去了,弹窗,然后显示乱码,我们需要执行sub_401000函数,所以将其跳转到该函数前一步,005E109B处,然后,还需要一个跳转调出messagebox,所以还要将下面的跳转跳转到第二个flag位置,因为flag是在lpMem + 1位置处弹出来的。
此时eax当中和栈中也如期出现了flag
查壳无壳,载入ida分析
上面部分是处理字符串得到flag,下面部分是处理文件,重点是上面部分,s和t是关键
查看s和t的值,py逆向写出脚本
s = 'c61b68366edeb7bdce3c6820314b7498'
flag=''
for i in range(len(s)):
a = -1
if i & 1:
a = 1
flag += (chr(ord(s[i]) + a))
print(flag)
然后将得到在字符串用SharifCTF{}包裹得到flag
下载附件,pyc文件,在线反编译
import base64
def encode(message):
s = ''
for i in message:
x = ord(i) ^ 32
x = x + 16
s += chr(x)
return base64.b64encode(s)
correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
flag = ''
print 'Input flag:'
flag = raw_input()
if encode(flag) == correct:
print 'correct'
else:
print 'wrong'
逆向写出脚本即可
import base64
correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
a = base64.b64decode(correct)
flag = ''
for i in range(len(a)):
x = a[i]
x -= 16
y = x ^ 32
flag += chr(y)
print(flag)
nctf{d3c0mpil1n9_PyC}
这个题,提示是个迷宫题,查壳无壳,ida分析
结构很复杂,f5代码很多,查找字符串
******* * **** * **** * *** # *** *** *** *********
上面这个就是迷宫了,分析伪代码得知这是一个88的迷宫
我们将迷宫打印出来,为了方便,我们将空格代替为o