buuCTF的wp
前言,主要是re方向的题,应该会持续更新,更新时间不定
re方向
1.easyre
签到题,直接用ida打开,shift+12查看字符串,如下
得到flag:flag{this_Is_a_EaSyRe}
2.reverse1
f5看关键代码如下
sub_1400111D1("input the flag:");
sub_14001128F("%20s", &Str1);
v3 = j_strlen(Str2);
if ( !strncmp(&Str1, Str2, v3) )//意思是输入的flag是str1,当str1和str2相等是就是正确的
sub_1400111D1("this is the right flag!\n");
else
sub_1400111D1("wrong flag\n");
sub_14001113B(&v5, &unk_140019D00);
return 0i64;
str2点开是{hello_world},然后上面有个点是要把o改为0,所以flag:flag{hell0_w0rld}
3.reverse2
打开IDA,F5看关键代码。
将圈出来的代码用R键,得到
图片中说错了,应该是要把flag中的i和r改为1
点开&flag,得到关键代码
.data:0000000000601080 flag db '{'//这个{也要用R键才看得到 ; DATA XREF: main+34↑r
.data:0000000000601080 ; main+44↑r ...
.data:0000000000601081 aHackingForFun db 'hacking_for_fun}',0
.data:0000000000601081 _data ends
flag中的i和r改为1,最后得到flag:flag{hack1ng_fo1_fun}
4.内涵的软件
根据提示可能是一个upx加壳的软件,用PEID打开
直接IDA打开查看字符串,得到flag:flag{49d3c93df25caad81232130f3d2ebfad}
5.新年快乐
用ida打开什么也没有,用PEID打开
发现是个upx加壳的东西,用upx解壳,将upx.exe和该文件一起放在D盘下,再在cmd里面执行命令。
原来21kb的文件变成了28kb,再用ida打开,得到重要代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
char Str2; // [esp+12h] [ebp-3Ah]
char Str1[2]; // [esp+20h] [ebp-2Ch]
char v6; // [esp+22h] [ebp-2Ah]
__main();
strcpy(&Str2, "HappyNewYear!");
*(_WORD *)Str1 = 0;
memset(&v6, 0, 0x1Eu);
printf("please input the true flag:");
scanf("%s", Str1);
if ( !strncmp(Str1, &Str2, strlen(&Str2)) )//str1是输入的flag,这个str1要和str2相等
result = puts("this is true flag!");
else
result = puts("wrong!");
return result;
}
所以flag就是:flag{HappyNewYear!}
6.[BJDCTF 2nd]guessgame
ida打开,得到flag:BJD{S1mple_ReV3r5e_W1th_0D_0r_IDA}
7.helloword
下载下来发现是个apk文件,直接解压,得到几个文件
可以把那个文件后缀名直接改txt,然后搜索flag,也可以用jeb软件打开,然后搜索字符串得到
flag{7631a988259a00816deda84afb29430a}
8.xor
考点 数组内异或
ida打开,找到主要函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // rsi
int result; // eax
signed int i; // [rsp+2Ch] [rbp-124h]
char v6[264]; // [rsp+40h] [rbp-110h]
__int64 v7; // [rsp+148h] [rbp-8h]
memset(v6, 0, 0x100uLL);
v3 = (char *)256;
printf("Input your flag:\n", 0LL);
get_line(v6, 256LL);//v6是输入的flag
if ( strlen(v6) != 33 )//输入的flag长度为33
goto LABEL_12;
for ( i = 1; i < 33; ++i )
v6[i] ^= v6[i - 1]; //数组异或,可以自己举三个数,然后进行异或,然后在看看怎么异或回去。
v3 = global;
if ( !strncmp(v6, global, 0x21uLL) ) //global要和异或v6相等,要自己去找global的值。
printf("Success", v3);
else
LABEL_12:
printf("Failed", v3);
result = __stack_chk_guard;
if ( __stack_chk_guard == v7 )
result = 0;
return result;
}
global为f\nk\fw&O.@\x11x\rZ;U\x11p\x19F\x1Fv"M#D\x0Eg\x06h\x0FG2O,可以在Python中用list改善一下’f’, ‘\n’, ‘k’, ‘\x0c’, ‘w’, ‘&’, ‘O’, ‘.’, ‘@’, ‘\x11’, ‘x’, ‘\r’, ‘Z’, ‘;’, ‘U’, ‘\x11’, ‘p’, ‘\x19’, ‘F’, ‘\x1f’, ‘v’, ‘“‘, ‘M’, ‘#’, ‘D’, ‘\x0e’, ‘g’, ‘\x06’, ‘h’, ‘\x0f’, ‘G’, ‘2’, ‘O’
开始写脚本
#include<stdio.h>
int main(void)
{
int i;
char a[]={'f', '\n', 'k', '\x0c', 'w', '&', 'O', '.', '@', '\x11', 'x', '\r', 'Z', ';', 'U', '\x11', 'p', '\x19', 'F', '\x1f', 'v', '"', 'M', '#', 'D', '\x0e', 'g', '\x06', 'h', '\x0f', 'G', '2', 'O'};
for(i=32;i!=0;i--)
{
a[i]=a[i]^a[i-1];
}
for(i=0;i<33;i++)
{
printf("%c",a[i]);
}
}
得到flag{QianQiuWanDai_YiTongJiangHu}
9.reverse3
用ida打开,找到主要函数
int v3; // edx
__int64 v4; // ST08_8
signed int j; // [esp+DCh] [ebp-ACh]
signed int i; // [esp+E8h] [ebp-A0h]
signed int v8; // [esp+E8h] [ebp-A0h]
char Dest[108]; // [esp+F4h] [ebp-94h]
char Str; // [esp+160h] [ebp-28h]
char v11; // [esp+17Ch] [ebp-Ch]
for ( i = 0; i < 100; ++i )
{
if ( (unsigned int)i >= 0x64 )
j____report_rangecheckfailure();
Dest[i] = 0;
}
sub_41132F("please enter the flag:");
sub_411375("%20s", &Str);//输入的flag
v0 = j_strlen(&Str);
v1 = (const char *)sub_4110BE(&Str, v0, &v11);//对输入的flag进行了没有改表的base编码,可以直接用网上在线base64解码。
strncpy(Dest, v1, 0x28u);//将v1给了Dest
v8 = j_strlen(Dest);
for ( j = 0; j < v8; ++j )//对Dest进行了一个简单加密。
Dest[j] += j;
v2 = j_strlen(Dest);
if ( !strncmp(Dest, Str2, v2) )//将Dest和str2比较,就是要一样
sub_41132F("rigth flag!\n");
else
sub_41132F("wrong flag!\n");
HIDWORD(v4) = v3;
LODWORD(v4) = 0;
return v4;
}
先找到str2=‘e3nifIH9b_C@n@dH’,用这个来得到,解密后的Dest,脚本如下
#include<stdio.h>
int main(void)
{
char a[]={'e', '3', 'n', 'i', 'f', 'I', 'H', '9', 'b', '_', 'C', '@', 'n', '@', 'd', 'H'};
int i;
for(i=0;i<16;i++)
{
a[i]=a[i]-i;
}
for(i=0;i<16;i++)
{
printf("%c",a[i]);
}
}
得到Dest=‘e2lfbDB2ZV95b3V9’,由于是未改表的base编码,所以可以直接用网上在线解码,得到flag{i_l0ve_you}
10.不一样的flag
考点,迷宫题。
直接打开,叫你输入1,2,3,4,分别代表上下左右。
有耐心的话可以一次一次试,只要输入对了,就会继续你输入,直到无论你输入那个数都是直接退出,将输入的数组合起来就是flag。
用ida查看源代码。
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char v3; // [esp+17h] [ebp-35h]
int v4; // [esp+30h] [ebp-1Ch]
int v5; // [esp+34h] [ebp-18h]
int v6; // [esp+38h] [ebp-14h]
int i; // [esp+3Ch] [ebp-10h]
char v8[12]; // [esp+40h] [ebp-Ch]
__main();
v4 = 0;
v5 = 0;
qmemcpy(&v3, _data_start__, 0x19u);
while ( 1 )
{
puts("you can choose one action to execute");
puts("1 up");
puts("2 down");
puts("3 left");
printf("4 right\n:");
scanf("%d", &v6);//你输入的数,也就是flag
————————————————————————————————————————————————
if ( v6 == 2 )
{
++v4;
}
else if ( v6 > 2 )
{
if ( v6 == 3 )
{
--v5;
}
else
{
if ( v6 != 4 )
LABEL_13:
exit(1);
++v5;
}
}
else
{
if ( v6 != 1 )
goto LABEL_13;
--v4;
}
for ( i = 0; i <= 1; ++i )
{
if ( *(&v4 + i) < 0 || *(&v4 + i) > 4 )
exit(1);
}
if ( v8[5 * v4 - 41 + v5] == 49 )
exit(1);
if ( v8[5 * v4 - 41 + v5] == 35 )
{
puts("\nok, the order you enter is the flag!");
exit(0);
}//这一整段就是要通过你输入的一个个数字,使最后一个if调键满足,那个35实际上就是‘#’,也就是迷宫的终点,而49就是迷宫中的‘1’,不能碰到。
——————————————————————————————————————————————————————————
}
}
真正的迷宫在字符串中
'*11110100001010000101111#'//25个数,盲猜5*5
用脚本画出迷宫
#include<stdio.h>
int main(void)
{
int a[5][5]={'*', '1', '1', '1', '1', '0', '1', '0', '0', '0', '0', '1', '0', '1', '0', '0', '0', '0', '1', '0', '1', '1', '1', '1', '#'};
int i,j;
for(i=0;i<5;i++)
{
for(j=0;j<5;j++)
{
printf("%c",a[i][j]);
}
printf("\n");
}
}
得到迷宫
*1111
01000
01010
00010
1111#
根据1,2,3,4四个反向得到flag{222441144222}
11.SimpleRev
关键代码如下,要熟悉ascall码表中AZ,az的十进制数
unsigned __int64 Decry()
{
char v1; // [rsp+Fh] [rbp-51h]
int v2; // [rsp+10h] [rbp-50h]
int v3; // [rsp+14h] [rbp-4Ch]
int i; // [rsp+18h] [rbp-48h]
int v5; // [rsp+1Ch] [rbp-44h]
char src[8]; // [rsp+20h] [rbp-40h]
__int64 v7; // [rsp+28h] [rbp-38h]
int v8; // [rsp+30h] [rbp-30h]
__int64 v9; // [rsp+40h] [rbp-20h]
__int64 v10; // [rsp+48h] [rbp-18h]
int v11; // [rsp+50h] [rbp-10h]
unsigned __int64 v12; // [rsp+58h] [rbp-8h]
v12 = __readfsqword(0x28u);
*(_QWORD *)src = 357761762382LL;//src='SLCDN'
v7 = 0LL;
v8 = 0;
v9 = 512969957736LL;//v9='wodah'
v10 = 0LL;
v11 = 0;
text = (char *)join(key3, &v9);// key3='kills' test=key3+v9=‘killshadow’
strcpy(key, key1);// Key1='ADSFK'
strcat(key, src);//Key='ADSFKNDCLS' 注意要倒过来
v2 = 0;
v3 = 0;
getchar();
v5 = strlen(key);
for ( i = 0; i < v5; ++i )
{
if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )
key[i] = key[v3 % v5] + 32;
++v3;
}//将key全部换为小写。
printf("Please input your flag:", src);
while ( 1 )
{
v1 = getchar();//V1就是输入的flag,根据下面的if条件只能算出v1的范围为64~90,97~122,涉及到下面%求余的整数部分的问题。
if ( v1 == 10 )
break;
if ( v1 == 32 )
{
++v2;
}
else
{
if ( v1 <= 96 || v1 > 122 )
{
if ( v1 > 64 && v1 <= 90 )
str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;//加密语句
}
else
{
str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
}
if ( !(v3 % v5) )
putchar(32);
++v2;
}
}
if ( !strcmp(text, str2) )//要求text=str2
puts("Congratulation!\n");
else
puts("Try again!\n");
return __readfsqword(0x28u) ^ v12;
}
感觉这道题有点问题,v1的范围有点没确定好,也可能是我忽略了某个地方。
脚本如下
#include<stdio.h>
int main(void)
{
char a[11]="ADSFKNDCLS";
char b[11]="killshadow";
char c[11]="\0";
int i=0,j=0;
for(i=0;i<11;i++,j++)
{
if( a[j % 11] > 64 && a[j % 11] <= 90)
a[i] = a[j % 10] + 32;//这个里面的j%11实际上就相当于i,只是在ida中打开变了样子。
printf("%c",a[i]);
}
printf("\n");
for(i=0;i<10;i++)
{
c[i]=(b[i]-97)+26+39-97+a[i];//因为不知道原加密语句求余的那个整数是多少所以只加了一个26,
if(c[i]>90)
{
c[i]=c[i]-26;
}//加上这个主要是要所有字母都是大写的,不然会有乱码
printf("%c",c[i]);
}
}
12.[BJDCTF 2nd]8086
根据文件名字,得知应该这是8086的32位汇编。
用ida打开,shift+12打开字符串,发现只有一句点进去,得到
查看重要代码如下
根据这个分析开始写脚本,如下
#include<stdio.h>
int main(void)
{
int i;
char a[]={']', 'U', '[', 'd', 'u', '~', '|', 't', '@', '{', 'z', '@', 'w', 'j', '.', '}', '.', '~', 'q', '@', 'g', 'j', 'z', '{', 'z', '@', 'w', 'z', 'q', 'W', '~', '/', 'b', ';'};
for(i=0;i<33;i++)
{
a[i]=a[i]^0x1f;
printf("%c",a[i]);
}
}
得到 flag:BJD{jack_de_hu1b1an_xuede_henHa0}
13.[GKCTF2020]Check_1n
这道题,首先明确一点,这是签到题,所以不会太难
先运行出来,发现是一个用c++写的一个代码电脑文件
↑ ↓ ← → 空格 进行操作
先开机,发现需要密码,打开ida,看字符串。
开机后发现有几个文件,其中有一个叫flag,打开
发现
解码内容为
Why don't you try the magic brick game
你为什么不试试魔法砖游戏
然后去玩魔法砖游戏
得到flag{f5dfd0f5-0343-4642-8f28-9adbb74c4ede}回头看看这
14.findit
下载下来发现是个apk文件,用jeb打开。
发现重要线索
this.findViewById(0x7F05003D).setOnClickListener(new View$OnClickListener(new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}, this.findViewById(0x7F05003E), new char[]{'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'}, this.findViewById(0x7F05003F))
提取有用信息
p v k q { m 1 6 4 6 7 5 2 6 2 0 3 3 l 4 m 4 9 l n p 7 p 9 m n k 2 8 k 7 5 } //实际上将apk文件解压,然后将文件classes.dex后缀改为txt,搜索‘{’也可以得到这个
分析jeb代码,发现好像和凯撒加密有一些关系,直接暴力破解,脚本如下
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
int main(void)
{
char table1[27]="abcdefghijklmnopqrstuvwxyz";
char table2[27]="ABCDEFJHIJKLMNOPQRSTUVWXYZ";
char str1[100]="pvkq{m164675262033l4m49lnp7p9mnk28k75}";
char str2[100]="";
int i,k,len;
len=strlen(str1);
for(k=0;k<26;k++)
{
for(i=0;i<len;i++)
{
if(str1[i]>=table1[k]&&str1[i]<='z')
{
str2[i]=str1[i]-k;
}
else if(str1[i]<table1[k]&&str1[i]>='a')
{
str2[i]=str1[i]+26-k;
}
else if(str1[i]>=table2[k]&&str1[i]<='Z')
{
str2[i]=str1[i]-k;
}
else if(str1[i]<table2[k]&&str1[i]>='A')
{
str2[i]=str1[i]+26-k;
}
else
{
str2[i]=str1[i];
}
}
printf("%s",str2);
printf("\n");
}
}
得到
pvkq{m164675262033l4m49lnp7p9mnk28k75}
oujp{l164675262033k4l49kmo7o9lmj28j75}
ntio{k164675262033j4k49jln7n9kli28i75}
mshn{j164675262033i4j49ikm7m9jkh28h75}
lrgm{i164675262033h4i49hjl7l9ijg28g75}
kqfl{h164675262033g4h49gik7k9hif28f75}
jpek{g164675262033f4g49fhj7j9ghe28e75}
iodj{f164675262033e4f49egi7i9fgd28d75}
hnci{e164675262033d4e49dfh7h9efc28c75}
gmbh{d164675262033c4d49ceg7g9deb28b75}
flag{c164675262033b4c49bdf7f9cda28a75}//这个就是flag
ekzf{b164675262033a4b49ace7e9bcz28z75}
djye{a164675262033z4a49zbd7d9aby28y75}
cixd{z164675262033y4z49yac7c9zax28x75}
bhwc{y164675262033x4y49xzb7b9yzw28w75}
agvb{x164675262033w4x49wya7a9xyv28v75}
zfua{w164675262033v4w49vxz7z9wxu28u75}
yetz{v164675262033u4v49uwy7y9vwt28t75}
xdsy{u164675262033t4u49tvx7x9uvs28s75}
wcrx{t164675262033s4t49suw7w9tur28r75}
vbqw{s164675262033r4s49rtv7v9stq28q75}
uapv{r164675262033q4r49qsu7u9rsp28p75}
tzou{q164675262033p4q49prt7t9qro28o75}
synt{p164675262033o4p49oqs7s9pqn28n75}
rxms{o164675262033n4o49npr7r9opm28m75}
qwlr{n164675262033m4n49moq7q9nol28l75}
也可以根据前几个字母,先算出位移量,到网上去在线解密。
15.[GXYCTF2019]luck_guy
自己ida打开,找到关键代码,找的过程就不说了
unsigned __int64 get_flag()
{
unsigned int v0; // eax
char v1; // al
signed int i; // [rsp+4h] [rbp-3Ch]
signed int j; // [rsp+8h] [rbp-38h]
__int64 s; // [rsp+10h] [rbp-30h]
char v6; // [rsp+18h] [rbp-28h]
unsigned __int64 v7; // [rsp+38h] [rbp-8h]
v7 = __readfsqword(40u);
v0 = time(0LL);
srand(v0);
for ( i = 0; i <= 4; ++i )
{
switch ( rand() % 200 )//就是随机取数了,所以叫luck_guy,实际上有用的case也就1,4,5。
{
case 1:
puts("OK, it's flag:");
memset(&s, 0, 0x28uLL); // 将s0清0
strcat((char *)&s, f1); // f1='GXY{do_not_'
strcat((char *)&s, &f2); // f2是剩下的那一半flag
printf("%s", &s);
break;
case 2:
printf("Solar not like you");
break;
case 3:
printf("Solar want a girlfriend");
break;
case 4:
v6 = 0;
s = 'fo`guci';
strcat(&f2, (const char *)&s);//将s给f2,注意要反序。
break;
case 5://就一个将f2的加密过程,算出来的f2就是另一半flag。
for ( j = 0; j <= 7; ++j )
{
if ( j % 2 == 1 )
v1 = *(&f2 + j) - 2;
else
v1 = *(&f2 + j) - 1;
*(&f2 + j) = v1;
}
break;
default:
puts("emmm,you can't find flag 23333");
break;
}
}
return __readfsqword(0x28u) ^ v7;
}
根据分析,写出脚本
#include<stdio.h>
int main(void)
{
int i;
char a[]={'i', 'c', 'u', 'g', '`', 'o', 'f', '\x7f'};
for(i=0;i<=7;i++)
{
if (i%2==1)
a[i]=a[i]-2;
else
a[i]=a[i]-1;
printf("%c",a[i]);
}
}
得到f2:hate_me} 和f1拼起来,得到GXY{do_not_hate_me}
16.简单注册器
下载下来是个apk文件,jeb打开,与14题一样操作,得到重要代码。
根据分析写出脚本如下
#include<stdio.h>
int main(void)
{
int m=0x1f,n=2,i=0,j;
char a[33]="dd2940c04462b4dd7c450528835cca15";
a[n] = ((char)(a[n] + a[3] - 50));
a[4] = ((char)(a[n] + a[5] - 0x30));
a[30] = ((char)(a[m] + a[9] - 0x30));
a[14] = ((char)(a[27] + a[28] - 97));
for(i=0;i<16;i++)
{
j=a[0x1f-i];
a[0x1f-i]=a[i];
a[i]=j;
}
printf("%s",a);
}
得到59acc538825054c7de4b26440c0999dd
所以flag{59acc538825054c7de4b26440c0999dd}
17.[GWCTF 2019]pyre
看题目,pyre,说明是python文件,下载下来是pyc,先反编译为py文件,在cmd里面输入命令(我11届极客大挑战的文章中中有也有一道题是反编译pyc文件)
C:\Users\hp>uncompyle6 -o C:\Users\hp\Downloads\attachment.py C:\Users\hp\Downloads\attachment.pyc
C:\Users\hp\Downloads\attachment.pyc --
# Successfully decompiled file
出现Successfully则表示成功。用python的IDLE打开这个py文件,得到源码,如下
print 'Welcome to Re World!'
print 'Your input1 is your flag~'
l = len(input1)
for i in range(l)://第一次for循环加密,注意i和1的区别,我就是因为把i看作了1,做了很久。
num = ((input1[i] + i) % 128 + 128) % 128
code += num//注意这一句,和c不太一样,它的意思是用num的值来填充code。
for i in range(l - 1)://第二个加密,就是数组内异或。
code[i] = code[i] ^ code[(i + 1)]
print code
code = ['\x1f', '\x12', '\x1d', '(', '0', '4', '\x01', '\x06', '\x14', '4', ',', '\x1b', 'U', '?', 'o', '6', '*', ':', '\x01', 'D', ';', '%', '\x13']
写出脚本
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
int main(void)
{
int i,len;
char code[23]={'\x1f', '\x12', '\x1d', '(', '0', '4', '\x01', '\x06', '\x14', '4', ',', '\x1b', 'U', '?', 'o', '6', '*', ':', '\x01', 'D', ';', '%', '\x13'};
len=strlen(code);
printf("%d",len);
printf("\n");
for(i=len-2;i!=-1;i--)//数组内异或解密
{
code[i]=code[i]^code[i+1];
}
for(i=0;i!=len;i++)
{
code[i]=code[i]-i;//这时候的一些code[i]的十进制值是负数
if(code[i]<0)//注意这个if条件,保证flag内字符的值在64~127内。
{
code[i]+=128;
}
printf("%c",code[i]);
}
}
得到flag:GWHT{Just_Re_1s_Ha66y!}
18.rsa
首先了解rsa是什么。
http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
https://zhuanlan.zhihu.com/p/44185847?utm_source=wechat_session
下载下来得到两个文件,一个文件是flag,一个是公钥。
用公钥在线分析网站,分解公钥
http://tool.chacuo.net/cryptrsakeyparse/,也可以用Linux的kali来分解公钥(某位大佬的博客https://www.cnblogs.com/jane315/p/13175850.html)
得到
n=C0332C5C64AE47182F6C1C876D42336910545A58F7EEFEFC0BCAAF5AF341CCDD
e=65537 (0x10001)
用Python将n转化为十进制
>>> a=int('C0332C5C64AE47182F6C1C876D42336910545A58F7EEFEFC0BCAAF5AF341CCDD',16)
>>> print(a)
86934482296048119190666062003494800588905656017203025617216654058378322103517
得到n=86934482296048119190666062003494800588905656017203025617216654058378322103517
在继续分解n得到p,q。网站:http://factordb.com/得到
p=285960468890451637935629440372639283459
q=304008741604601924494328155975272418463
然后写脚本,这里是直接用的别人的,需要注意的是需要安装两个库。
import gmpy2
import rsa
e=65537
n=86934482296048119190666062003494800588905656017203025617216654058378322103517
p=285960468890451637935629440372639283459
q=304008741604601924494328155975272418463
phin = (p-1) * (q-1)
d=gmpy2.invert(e, phin)
key=rsa.PrivateKey(n,e,int(d),p,q)
with open("flag.enc","rb") as f://这里是打开那个flag.net文件
f=f.read()
print(rsa.decrypt(f,key))
得到flag{decrypt_256}
19.JustRE
文件下载下来,打开发现
说是要你一直点就会出flag。不过它说的还真是真的。
正确解法,放到ida里面,shift +12。
找到其所在函数,f5看伪代码。
不知道sprintf的意思,网上搜一搜,https://blog.csdn.net/wangkeyen/article/details/81942918
aBjdDD2069a4579就是BJD{%d%d2069a45792d233ac}
而 sprintf(&String, aBjdDD2069a4579, 19999, 0);就是要你把19999,0分别是那两个%d
得到flag{1999902069a45792d233ac}
20.[2019红帽杯]easyRE
下载文件用ida打开,shift+12;
找到重要代码
unsigned __int64 v54; // [rsp+108h] [rbp-18h]
v54 = __readfsqword(0x28u);
v12 = 73;
v13 = 111;
v14 = 100;
v15 = 108;
v16 = 62;
v17 = 81;
v18 = 110;
v19 = 98;
v20 = 40;
v21 = 111;
v22 = 99;
v23 = 121;
v24 = 127;
v25 = 121;
v26 = 46;
v27 = 105;
v28 = 127;
v29 = 100;
v30 = 96;
v31 = 51;
v32 = 119;
v33 = 125;
v34 = 119;
v35 = 101;
v36 = 107;
v37 = 57;
v38 = 123;
v39 = 105;
v40 = 121;
v41 = 61;
v42 = 126;
v43 = 121;
v44 = 76;
v45 = 64;
v46 = 69;
v47 = 67;
memset(v48, 0, sizeof(v48));
v49 = 0;
v50 = 0;
sub_4406E0(0LL, v48, 37LL);
v50 = 0;
if ( sub_424BA0(v48) == 36 )
{
for ( i = 0; i < (unsigned __int64)sub_424BA0(v48); ++i )
{
if ( (unsigned __int8)(v48[i] ^ i) != *(&v12 + i) )//v48是自己输入的,*(&v12 + i)就相当于一个数组,从v12到v47,一共36个数。简单的异或。
{
result = 4294967294LL;
goto LABEL_13;
}
}
sub_410CC0("continue!");
memset(&v51, 0, 0x40uLL);
v53 = 0;
sub_4406E0(0LL, &v51, 64LL);
v52 = 0;
if ( sub_424BA0(&v51) == 39 )
{
v1 = sub_400E44(&v51);
v2 = sub_400E44(v1);
v3 = sub_400E44(v2);
v4 = sub_400E44(v3);
v5 = sub_400E44(v4);
v6 = sub_400E44(v5);
v7 = sub_400E44(v6);
v8 = sub_400E44(v7);
v9 = sub_400E44(v8);
v10 = sub_400E44(v9);
if ( !(unsigned int)sub_400360(v10, off_6CC090) )
{
sub_410CC0("You found me!!!");
sub_410CC0("bye bye~");
}
result = 0LL;
}
else
{
result = 4294967293LL;
}
}
else
{
result = 0xFFFFFFFFLL;
}
LABEL_13:
if ( __readfsqword(0x28u) != v54 )
sub_444020();
return result;
}
先看这一部分
if ( sub_424BA0(v48) == 36 )
{
for ( i = 0; i < (unsigned __int64)sub_424BA0(v48); ++i )
{
if ( (unsigned __int8)(v48[i] ^ i) != *(&v12 + i) )//v48是自己输入的,*(&v12 + i)就相当于一个数组,从v12到v47,一共36个数。简单的异或。
{
result = 4294967294LL;
goto LABEL_13;
}
}
写出脚本
#include<stdio.h>
int main(void)
{
int a[36]={73,111,100,108,62,81,110,98,40,111,99,121,127,121,46,105,127,100,96,51,119,125,119,101,107,57,123,105,121,61,126,121,76,64,69,67};
int i;
for(i=0;i<36;i++)
{
a[i]=a[i]^i;
printf("%c",a[i]);
}
}
得到
它说:前四个字符是“flag”,现在还不知道意思。
继续看
memset(&v51, 0, 0x40uLL);//定义了一个数组叫v51
v53 = 0;
sub_4406E0(0LL, &v51, 64LL);
v52 = 0;
if ( sub_424BA0(&v51) == 39 )//v61长度是39
{
v1 = sub_400E44(&v51);//对v51base64编码
v2 = sub_400E44(v1);//对v1base64编码
v3 = sub_400E44(v2);
v4 = sub_400E44(v3);
v5 = sub_400E44(v4);
v6 = sub_400E44(v5);
v7 = sub_400E44(v6);
v8 = sub_400E44(v7);
v9 = sub_400E44(v8);
v10 = sub_400E44(v9);
if ( !(unsigned int)sub_400360(v10, off_6CC090) )将v10和off_6CC090对比。
{
sub_410CC0("You found me!!!");
sub_410CC0("bye bye~");
}
整个过程就是一个不断base64编码的过程,进行了10次,得到了off_6CC090,也就是
Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFpOYmtKVVZtcEtTMUl5VGtsaVJtUk9ZV3hhZVZadGVHdFRNVTVYVW01T2FGSnRVbGhhVjNoaFZWWmtWMXBFVWxSTmJFcElWbTAxVDJGV1NuTlhia0pXWWxob1dGUnJXbXRXTVZaeVdrWm9hVlpyV1hwV1IzaGhXVmRHVjFOdVVsWmlhMHBZV1ZSR1lWZEdVbFZTYlhSWFRWWndNRlZ0TVc5VWJGcFZWbXR3VjJKSFVYZFdha1pXWlZaT2NtRkhhRk5pVjJoWVYxZDBhMVV3TlhOalJscFlZbGhTY1ZsclduZGxiR1J5VmxSR1ZXSlZjRWhaTUZKaFZqSktWVkZZYUZkV1JWcFlWV3BHYTFkWFRrZFRiV3hvVFVoQ1dsWXhaRFJpTWtsM1RVaG9hbEpYYUhOVmJUVkRZekZhY1ZKcmRGTk5Wa3A2VjJ0U1ExWlhTbFpqUldoYVRVWndkbFpxUmtwbGJVWklZVVprYUdFeGNHOVhXSEJIWkRGS2RGSnJhR2hTYXpWdlZGVm9RMlJzV25STldHUlZUVlpXTlZadE5VOVdiVXBJVld4c1dtSllUWGhXTUZwell6RmFkRkpzVWxOaVNFSktWa1phVTFFeFduUlRhMlJxVWxad1YxWnRlRXRXTVZaSFVsUnNVVlZVTURrPQ==
直接网上解码10次,发现是https://bbs.pediy.com/thread-254172.htm,居然是一个网站。。。。无情被欺骗。最后通过看wp,发现真正的算法在
得到代码
unsigned __int64 __fastcall sub_400D35(__int64 a1, __int64 a2)
{
__int64 v2; // rdx
__int64 v3; // rdx
__int64 v4; // rdx
unsigned __int64 result; // rax
unsigned __int64 v6; // rt1
unsigned int v7; // [rsp+Ch] [rbp-24h]
signed int i; // [rsp+10h] [rbp-20h]
signed int j; // [rsp+14h] [rbp-1Ch]
unsigned int v10; // [rsp+24h] [rbp-Ch]
unsigned __int64 v11; // [rsp+28h] [rbp-8h]
v11 = __readfsqword(0x28u);
v7 = sub_43FD20() - qword_6CEE38;
for ( i = 0; i <= '\x04�'; ++i )
{
sub_40F790(v7);
sub_40FE60(v7, a2, v2);
sub_40FE60(v7, a2, v3);
v7 = (unsigned __int64)sub_40FE60(v7, a2, v4) ^ 2557891634;
}
v10 = v7;
if ( ((unsigned __int8)v7 ^ byte_6CC0A0[0]) == 'f' && (HIBYTE(v10) ^ (unsigned __int8)byte_6CC0A3) == 'g' )//这里的f和g是不是有点熟悉,再联系一下前面的提示:前四个字符是“flag”,通过这个来求key。
{
for ( j = 0; j <= 24; ++j )
sub_410E90((unsigned __int8)(byte_6CC0A0[j] ^ *((_BYTE *)&v10 + j % 4)));//加密算法,key实际上就是那个v10,通过j % 4,又可以知道,v10只有4个。
}
v6 = __readfsqword(0x28u);
result = v6 ^ v11;
if ( v6 != v11 )
sub_444020();
return result;
}
byte_6CC0A0[25]={0x40,0x35,0x20,0x56,0x5d,0x18,0x22,0x45,0x17,0x2f,0x24,0x6e,0x62,0x3c,0x27,0x54,0x48,0x6c,0x24,0x6e,0x72,0x3c,0x32,0x45,0x5b};
写出脚本
#include<stdio.h>
int main(void)
{
int a[36]={73,111,100,108,62,81,110,98,40,111,99,121,127,121,46,105,127,100,96,51,119,125,119,101,107,57,123,105,121,61,126,121,76,64,69,67};
int key[4]={'f','l','a','g'} ;
int byte_6CC0A0[25]={0x40,0x35,0x20,0x56,0x5d,0x18,0x22,0x45,0x17,0x2f,0x24,0x6e,0x62,0x3c,0x27,0x54,0x48,0x6c,0x24,0x6e,0x72,0x3c,0x32,0x45,0x5b};
int i,j;
for(i=0;i<36;i++)
{
a[i]=a[i]^i;
printf("%c",a[i]);
}
printf("\n");
for(i=0;i<4;i++)//通过前4个字符,也就是flag,求出key。
{
key[i]=key[i]^byte_6CC0A0[i];
}
for(j=0;j<25;j++)//用key来解密。
{
byte_6CC0A0[j]=byte_6CC0A0[j]^key[j%4];
printf("%c",byte_6CC0A0[j]);
}
}
得到flag{Act1ve_Defen5e_Test}
21.[ACTF新生赛2020]easyre
下载下来用PEID打开,发现是upx加壳的文件
由于前面已经介绍过upx解壳了,就不写过程了。
然后将文件拖入ida,shift+12,ctrl+x,一套操作。得到关键代码
__main();
v4 = 42;
v5 = 70;
v6 = 39;
v7 = 34;
v8 = 78;
v9 = 44;
v10 = 34;
v11 = 40;
v12 = 73;
v13 = 63;
v14 = 43;
v15 = 64;//一个长度为12的数组。
printf("Please input:");
scanf("%s", &v19);
if ( v19 != 'A' || v20 != 'C' || v21 != 'T' || v22 != 'F' || v23 != '{' || v27 != '}' )//这里是r键过后的,从这可以知道v24~v26是我们需要的东西
return 0;
v16 = v24;
v17 = v25;
v18 = v26;
for ( i = 0; i <= 11; ++i )
{
if ( *(&v4 + i) != _data_start__[*((char *)&v16 + i) - 1] )// _data_start_是一个表,类似于base64编码表。*((char *)&v16 + i)是ACTF{}大括号中的内容。只不过这里我也有点疑惑,不应该只有3个吗。
return 0;
}
printf("You are correct!");
return 0;
}
写出脚本,这个脚本体现了一个从一个字符串中找某个字符的思想
#include<stdio.h>
int main(void)
{
int a[120]={'~', '}', '|', '{', 'z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '/', '.', '-', ',', '+', '*', ')', '(',0x27, '&', '%', '$', '#', ' ', '!', '"'};//a就是那个_data_start__
int v4[12]={42,70,39,34,78,44,34,40,73,63,43,64};
int i,j;
for(i=0;i<12;i++)
{
for(j=0;j<=120;j++)
{
if(v4[i]==a[j])
{
printf("%c",j+1);//这个j+1是因为_data_start__[*((char *)&v16 + i) - 1]中的-1
break;
}
}
}
}
得到flag{U9X_1S_W6@T?}
22.[SUCTF2019]SignIn
文件下载下来直接用ida打开,一套操作找到关键代码
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char v4; // [rsp+0h] [rbp-4A0h]
char v5; // [rsp+10h] [rbp-490h]
char v6; // [rsp+20h] [rbp-480h]
char v7; // [rsp+30h] [rbp-470h]
char v8; // [rsp+40h] [rbp-460h]
char v9; // [rsp+B0h] [rbp-3F0h]
unsigned __int64 v10; // [rsp+498h] [rbp-8h]
v10 = __readfsqword(0x28u);
puts("[sign in]");
printf("[input your flag]: ", a2);
__isoc99_scanf("%99s", &v8);
sub_96A(&v8, &v9);
__gmpz_init_set_str(&v7, "ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35", 16LL);
__gmpz_init_set_str(&v6, &v9, 16LL);
__gmpz_init_set_str(&v4, "103461035900816914121390101299049044413950405173712170434161686539878160984549", 10LL);
__gmpz_init_set_str(&v5, "65537", 10LL);
__gmpz_powm(&v6, &v6, &v5, &v4);
if ( (unsigned int)__gmpz_cmp(&v6, &v7) )
puts("GG!");
else
puts("TTTTTTTTTTql!");
return 0LL;
}
发现是一个rsa加密, __gmpz_init_set_str 函数是GNU 高精度算法库。
c=0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35//密文
n=103461035900816914121390101299049044413950405173712170434161686539878160984549
e=65537
先得到q,p。
q=282164587459512124844245113950593348271
p=366669102002966856876605669837014229419
用脚本跑出来
from Crypto.Util.number import *
import gmpy2
p = 282164587459512124844245113950593348271
q = 366669102002966856876605669837014229419
e = 65537
c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
n = q*p
phi = (q-1)*(p-1)
d = gmpy2.invert(e,phi)
m = gmpy2.powmod(c,d,n)
flag = long_to_bytes(m)
print(flag)
得到flag{Pwn_@_hundred_years}
23.Youngter-drive
首先文件下载下来,PEID打开,发现upx加壳了。
解壳
再用ida打开,找到主函数main_0,一进去就看得到,点进去在f5。
int __thiscall main_0(void *this)
{
HANDLE v2; // [esp+D0h] [ebp-14h]
HANDLE hObject; // [esp+DCh] [ebp-8h]
sub_4110FF(this);
::hObject = CreateMutexW(0, 0, 0);
j_strcpy(Dest, &Source);
hObject = CreateThread(0, 0, StartAddress, 0, 0, 0);
v2 = CreateThread(0, 0, sub_41119F, 0, 0, 0);
CloseHandle(hObject);
CloseHandle(v2);
while ( dword_418008 != -1 )
;
sub_411190();
CloseHandle(::hObject);
return 0;
}
这里是查阅的一些函数
CreateThread和CloseHandle:CreateThread是一种微软在Windows API中提供了建立新的线程的函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,线程对象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象。
Sleep:Sleep函数可以使计算机程序(进程,任务或线程)进入休眠,使其在一段时间内处于非活动状态。当函数设定的计时器到期,或者接收到信号、程序发生中断都会导致程序继续执行。
本来线程是同时进行的,然而这一道题就是通过sleep这个函数让线程交替进行了,导致了偶数位没进行加密,奇数位进行了加密。
下面来分析这个主函数
然后主要分析这两个线程
进入那个加密函数,报错无法看源代码。
说是栈顶sp有问题,然后找到该函数位置。
修改sp
然后得到加密函数。
char *__cdecl sub_411940(int a1, int a2)//a1是那个source(你输入的flag),a2是那个dword_418008(29)
{
char *result; // eax
char v3; // [esp+D3h] [ebp-5h]
v3 = *(_BYTE *)(a2 + a1);//相当与a[a2],source[a2]。
if ( (v3 < 97 || v3 > 122) && (v3 < 65 || v3 > 90) )
exit(0);//这里是必须要你输入的flag是是A~Z,a~z。
if ( v3 < 97 || v3 > 122 )//如果source[a2]是大写字母,就这样加密
{
result = off_418000[0];
*(_BYTE *)(a2 + a1) = off_418000[0][*(char *)(a2 + a1) - 38];
}
else//如果source[a2]是小写字母,就这样加密
{
result = off_418000[0];
*(_BYTE *)(a2 + a1) = off_418000[0][*(char *)(a2 + a1) - 96];
}
return result;
}
注意这一句
off_418000[0][*(char *)(a2 + a1) - 96];等价于off_418000[*(a2 + a1) - 96]//这个*(a2+a1)是未加密的,也就是你要求的flag。
一些数据
off_418000[]=QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm
加密后的*(_BYTE *)(a2 + a1)=TOiZiZtOrYaToUwPnToBsOaOapsyS
然后开始写脚本,
#include<stdio.h>
int main(void)
{
char str[30]="TOiZiZtOrYaToUwPnToBsOaOapsyS";//off_418004
char str1[53]="QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";//off_418000
char flag[30];
int a[30];
int b[30];
int i,j,m=0;
for(i=0;i<29;i++)
{
if(i%2==0)
flag[i]=str[i];
}
for(i=0;i<29;i++)
{
for(j=0;j<52;j++)
{
if(str[i]==str1[j])
{
// printf("%d ",j);
if(j<27)//这个27是算出来的,是决定是字母大写,字母小写的临界值
{
flag[m]=j+96;
m++;
}
else
{
flag[m]=j+38;
m++;
}
}
}
}
for(i=0;i<29;i++)//这个for循环就是因为两个线程交替进行的原因,偶数位不加密,奇数位要加密
{
if(i%2==0)
flag[i]=str[i];
}
for(i=0;i<29;i++)
{
printf("%c",flag[i]);
}
}
网上找的Python脚本如下
str="0abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
text1='TOiZiZtOrYaToUwPnToBsOaOapsyS'
text2='QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
flag=''
s=0
for i in range(len(text1)):
if(i%2==0):
flag+=text1[i]
else:
s=text2.index(text1[i])
flag+=str[s]
print(flag)
得到ThisisthreadofwindowshahaIsES,但这不是flag,真正的flag还要在后面加一个E(我也不知道为什么),得到flag{ThisisthreadofwindowshahaIsESE}
24.[GUET-CTF2019]re
文件下载下来用Exeinfo PE打开
发现是upx加壳,解壳
IDA打开,找到关键代码
去看看加密函数
_BOOL8 __fastcall sub_4009AE(char *a1)
{
if ( 1629056 * *a1 != 166163712 )
return 0LL;
if ( 6771600 * a1[1] != 731332800 )
return 0LL;
if ( 3682944 * a1[2] != 357245568 )
return 0LL;
if ( 10431000 * a1[3] != 1074393000 )
return 0LL;
if ( 3977328 * a1[4] != 489211344 )
return 0LL;
if ( 5138336 * a1[5] != 518971936 )
return 0LL;
if ( 7532250 * a1[7] != 406741500 )
return 0LL;
if ( 5551632 * a1[8] != 294236496 )
return 0LL;
if ( 3409728 * a1[9] != 177305856 )
return 0LL;
if ( 13013670 * a1[10] != 650683500 )
return 0LL;
if ( 6088797 * a1[11] != 298351053 )
return 0LL;
if ( 7884663 * a1[12] != 386348487 )
return 0LL;
if ( 8944053 * a1[13] != 438258597 )
return 0LL;
if ( 5198490 * a1[14] != 249527520 )
return 0LL;
if ( 4544518 * a1[15] != 445362764 )
return 0LL;
if ( 3645600 * a1[17] != 174988800 )
return 0LL;
if ( 10115280 * a1[16] != 981182160 )
return 0LL;
if ( 9667504 * a1[18] != 493042704 )
return 0LL;
if ( 5364450 * a1[19] != 257493600 )
return 0LL;
if ( 13464540 * a1[20] != 767478780 )
return 0LL;
if ( 5488432 * a1[21] != 312840624 )
return 0LL;
if ( 14479500 * a1[22] != 1404511500 )
return 0LL;
if ( 6451830 * a1[23] != 316139670 )
return 0LL;
if ( 6252576 * a1[24] != 619005024 )
return 0LL;
if ( 7763364 * a1[25] != 372641472 )
return 0LL;
if ( 7327320 * a1[26] != 373693320 )
return 0LL;
if ( 8741520 * a1[27] != 498266640 )
return 0LL;
if ( 8871876 * a1[28] != 452465676 )
return 0LL;
if ( 4086720 * a1[29] != 208422720 )
return 0LL;
if ( 9374400 * a1[30] == 515592000 )
return 5759124 * a1[31] == 719890500;
return 0LL;
}
直接除过去就能得到flag,只不过题有点问题,没有a[6],看了其他wp是1。
脚本如下,比较痛苦。
#include<stdio.h>
int main(void)
{
long long a1[33];
int i;
a1[0] = 166163712/1629056;
a1[1] = 731332800/6771600;
a1[2] = 357245568/3682944;
a1[3] = 1074393000/10431000;
a1[4] = 489211344/3977328;
a1[5] = 518971936/5138336;
a1[7] = 406741500/7532250;
a1[8] = 294236496/5551632;
a1[9] = 177305856/3409728;
a1[10] = 650683500/13013670;
a1[11] = 298351053/6088797;
a1[12] = 386348487/7884663;
a1[13] = 438258597/8944053;
a1[14] = 249527520/5198490;
a1[15] = 445362764/4544518;
a1[17] = 174988800/3645600;
a1[16] = 981182160/10115280;
a1[18] = 493042704/9667504;
a1[19] = 257493600/5364450;
a1[20] = 767478780/13464540;
a1[21] = 312840624/5488432;
a1[22] = 1404511500/14479500;
a1[23] = 316139670/6451830;
a1[24] = 619005024/6252576;
a1[25] = 372641472/7763364;
a1[26] = 373693320/7327320;
a1[27] = 498266640/8741520;
a1[28] = 452465676/8871876;
a1[29] = 208422720/4086720;
a1[30] =515592000/9374400;
a1[31] =719890500/5759124;
for(i=0;i<33;i++)
{
printf("%c",a1[i]);
}
return 0;
}
得到flag{e165421110ba03099a1c039337}
25.[FlareOn4]login
文件下载下来是一个html文件,打开看源代码
<!DOCTYPE Html />
<html>
<head>
<title>FLARE On 2017</title>
</head>
<body>
<input type="text" name="flag" id="flag" value="Enter the flag" />
<input type="button" id="prompt" value="Click to check the flag" />
<script type="text/javascript">
document.getElementById("prompt").onclick = function () {
var flag = document.getElementById("flag").value;
var rotFlag = flag.replace(/[a-zA-Z]/g, function(c){return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});//这个就是加密过程。
if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) {
alert("Correct flag!");
} else {
alert("Incorrect flag, rot again");
}
}
</script>
</body>
</html>
上面的加密过程由于没学JS有点看不懂,有点像C语言的简写条件(?:)条件语句的简写。但是看到了’a’,’z’,’A’,’Z’,就用凯撒脚本爆破了一下。
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
int main(void)
{
char table1[27]="abcdefghijklmnopqrstuvwxyz";
char table2[27]="ABCDEFJHIJKLMNOPQRSTUVWXYZ";
char str1[100]="PyvragFvqrYbtvafNerRnfl@syner-ba.pbz";
char str2[100]="";
int i,k,len;
len=strlen(str1);
for(k=0;k<26;k++)
{
for(i=0;i<len;i++)
{
if(str1[i]>=table1[k]&&str1[i]<='z')
{
str2[i]=str1[i]-k;
}
else if(str1[i]<table1[k]&&str1[i]>='a')
{
str2[i]=str1[i]+26-k;
}
else if(str1[i]>=table2[k]&&str1[i]<='Z')
{
str2[i]=str1[i]-k;
}
else if(str1[i]<table2[k]&&str1[i]>='A')
{
str2[i]=str1[i]+26-k;
}
else
{
str2[i]=str1[i];
}
}
printf("%s",str2);
printf("\n");
}
}
得到
PyvragFvqrYbtvafNerRnfl@syner-ba.pbz
OxuqzfEupqXasuzeMdqQmek@rxmdq-az.oay
NwtpyeDtopWzrtydLcpPldj@qwlcp-zy.nzx
MvsoxdCsnoVyqsxcKboOkci@pvkbo-yx.myw
LurnwcBrmnUxprwbJanNjbh@oujan-xw.lxv
KtqmvbAqlmTwoqvaIzmMiag@ntizm-wv.kwu
JspluaZpklSvnpuzHylLhzf@mshyl-vu.jvt
IroktzYojkRumotyGxkKgye@lrgxk-ut.ius
HqnjsyXnijQtlnsxFwjJfxd@kqfwj-ts.htr
GpmirxWmhiPskmrwEviIewc@jpevi-sr.gsq
FolhqwVlghOrjlqvDuhHdvb@ioduh-rq.frp
EnkgpvUkfgNqikpuCtgGcua@hnctg-qp.eqo
DmjfouTjefMphjotBsfFbtz@gmbsf-po.dpn
ClientSideLoginsAreEasy@flare-on.com//flag
BkhdmsRhcdKnfhmrZqdDzrx@ekzqd-nm.bnl
AjgclrQgbcJmeglqYpcCyqw@djypc-ml.amk
ZifbkqPfabIldfkpXobBxpv@cixob-lk.zlj
YheajpOezaHkcejoWnaAwou@bhwna-kj.yki
XgdzioNdyzGjbdinVmzZvnt@agvmz-ji.xjh
WfcyhnMcxyFiachmUlyYums@zfuly-ih.wig
VebxgmLbwxEhzbglTkxXtlr@yetkx-hg.vhf
UdawflKavwDgyafkSjwWskq@xdsjw-gf.uge
TczvekJzuvCfxzejRivVrjp@wcriv-fe.tfd
SbyudjIytuBewydiQhuUqio@vbqhu-ed.sec
RaxtciHxstAdvxchPgtTphn@uapgt-dc.rdb
QzwsbhGwrsZcuwbgOfsSogm@tzofs-cb.qca
感觉实际上那个加密就是位移为13的凯撒加密。得到flag{ClientSideLoginsAreEasy@flare-on.com}
26.[ACTF新生赛2020]usualCrypt
文件直接用IDA打开,然后找到关键代码部分
然后看加密函数
换表函数
大写字母转小写字母,小写字母转大写字母函数。
写出脚本
脚本1,大写字母转小写字母,小写字母转大写字母
#include<stdio.h>
int main(void)
{
char a[37]="zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9";
int i;
for(i=0;i<36;i++)
{
if(a[i]>='a'&&a[i]<='z')
{
a[i]=a[i]-32;
}
else if(a[i]>='A'&&a[i]<='Z')
{
a[i]=a[i]+32;
}
else
{
a[i]=a[i];
}
printf("%c",a[i]);
}
}
得到ZmxhZ3tiGNXlXjHfaDTzN2FfK3LycRTpc2L9,由于不想去改表了所以弄了个Python的脚本。
import base64
import string
str1 = "ZmxhZ3tiGNXlXjHfaDTzN2FfK3LycRTpc2L9"
string1 = "ABCDEFQRSTUVWXYPGHIJKLMNOZabcdefghijklmnopqrstuvwxyz0123456789+/"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
print (base64.b64decode(str1.translate(str.maketrans(string1,string2))))
得到flag{bAse64_h2s_a_Surprise}
27.[GWCTF 2019]xxor
直接IDA打开,找到关键代码,分析过程也在里面了。
一些资料
#define LOWORD(l) ((WORD)((DWORD_PTR)(l) & 0xffff))
#define HIWORD(l) ((WORD)((DWORD_PTR)(l) >> 16))
这是baiwindef.h头文件中对宏LOWORD和HIWORD的定义。du
作用分别是取出无符号长整型zhi参数dao的高16位和低16位。
因为一zhuan个长整型占32位,其中高低16位的值可能有不同的意义,需要通过这2个宏分别取出来使用。取出来的结果是一个无符号短整型的值。
其原理正如定义那样,取低16位的宏LOWORD使用按位与操作符与数字0xffff运算,而数字0xffff是一个低16位全为1的数字,那么对其位与操作可以得到参数的低16位。
而取高16位的宏HIWORD则更简单,只需将参数右移16位,剩下的就是原高16位的值了。
脚本如下
#include<stdio.h>
int main(void)
{
unsigned int v7[6];
unsigned int a2[4]={2,2,3,4};
unsigned int i,j,v5=0,v4,v3;//注意,这里必须是unsigned int代表无符号整型。
int v6[6];
v7[0]=3746099070;
v7[1]=550153460;
// V7[3]+V7[4]=4201428739;
// V7[3]-V7[4]=-1103824215;
// V7[2]-V7[3]=2225223423;
v7[2]=3774025685;
v7[3]=1548802262;
v7[4]=2652626477;
v7[5]=2230518816;
for(i=0;i<6;i++)
{
printf("%#x ",v7[i]);
}
for(j=0;j<5;j+=2)
{
v3=v7[j];
v4=v7[j+1];
v5 = 1166789954*0x40;
for(i=0;i<=63;i++)
{
v4 -=(v3 + v5 + 20) ^ ((v3 << 6) + a2[2]) ^ ((v3 >> 9) + a2[3]) ^ 0x10;
v3 -= (v4 + v5 + 11) ^ ((v4 << 6) + a2[0]) ^ ((v4 >> 9) + a2[1]) ^ 0x20;
v5 -= 1166789954;
}
v7[j]=v3;
v7[j+1]=v4;
}
printf("\n");
for(i=0;i<6;i++)
{
printf("%x",v7[i]);
}
printf("\n");
for (int i = 0; i < 6; ++i)
/*将整型数组作为字符输出,注意计算机小端排序*/
printf("%c%c%c", *((char*)&v7[i] + 2), *((char*)&v7[i] + 1), *(char*)&v7[i]);
}
得到
然后脚本提到的小端排序是指
flag{re_is_great!}
28.[HDCTF2019]Maze
文件下载下来用Exeinfo PE打开
解壳过程因为前面题已经有了,所以略过,解壳后的程序用IDA打开,然后发现有一个花指令
去花(怎么去花指令可以看看我那篇关于花指令的文章)
改E8为90
然后p键
f5看伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [esp+10h] [ebp-14h]
char v5[16]; // [esp+14h] [ebp-10h] BYREF
sub_401140(aGoThroughTheMa);
scanf("%14s", v5);
for ( i = 0; i <= 13; ++i )
{
switch ( v5[i] )//上下左右键分别为wsad。
{
case 'a':
--*(_DWORD *)asc_408078;
break;
case 'd':
++*(_DWORD *)asc_408078;
break;
case 's':
--dword_40807C;
break;
case 'w':
++dword_40807C;
break;
default:
continue;
}
}
if ( *(_DWORD *)asc_408078 == 5 && dword_40807C == -4 )//这里是通过你的移动然后改变了asc_408078(初始值是7),dword_40807C,然后如果这两个值满足这个条件,就是说明到了终点
{
sub_401140(aCongratulation);
sub_401140(aHereIsTheFlagF);
}
else
{
sub_401140(aTryAgain);
}
return 0;
}
shift+12,shift+e,得到地图,
脚本画出地图
#include<stdio.h>
//ssaaasaassdddw
int main(void)
{
int a[7][10]={42, 42, 42, 42, 42, 42, 42, 43, 42, 42,
42, 42, 42, 42, 42, 42, 42, 32, 42, 42,
42, 42, 42, 42, 32, 32, 32, 32, 42, 42,
42, 42, 32, 32, 32, 42, 42, 42, 42, 42,
42, 42, 32, 42, 42, 70, 42, 42, 42, 42,
42, 42, 32, 32, 32, 32, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42, };
int i,j;
for(i=0;i<7;i++)
{
for(j=0;j<10;j++)
{
printf("%c",a[i][j]);
}
printf("\n");
}
}
地图7*10的地图
*******+**
******* **
**** **
** *****
** **F****
** ****
**********
得到flag{ssaaasaassdddw}
29.[WUSTCTF2020]level1
文件下载下来有两个,用IDA打开level1,找到关键代码。
开始写脚本
#include<stdio.h>
int main(void)
{
int a[20]={0,198,232,816,200,1536,300,6144,984,51200,570,92160,1200,565248,756,1474560,800,6291456,1782,65536000};//注意a[0]设置的值为0;
int i;
for(i=1;i<20;i++)
{
if(i%2==0)
a[i]=a[i]/i;
else
a[i]=a[i]>>i;
}
for(i=1;i<20;i++)
{
printf("%c",a[i]);
}
}
得到flag{d9-dE6-20c}
30.[WUSTCTF2020]level2
文件下载下来用Exeinfo PE打开,发现是upx加壳
然后解壳
再用IDA打开文件,直接就可以看到flag{Just_upx_-d}
31.[FlareOn4]IgniteMe
这道题需要IDA静态调试和x32dbg动态调试一起。
先放到IDA里面
在继续到x32dbg里面看主函数框架。
然后进入那个比较函数。
现在就来动调找v4的值,在x32dbg里面进入那个比较函数之后。
得到v4=4,现在开始写脚本,如下。
#include<stdio.h>
int main(void)
{
int v4[40]={ 0x0D, 0x26, 0x49, 0x45, 0x2A, 0x17, 0x78, 0x44, 0x2B, 0x6C,
0x5D, 0x5E, 0x45, 0x12, 0x2F, 0x17, 0x2B, 0x44, 0x6F, 0x6E,
0x56, 0x09, 0x5F, 0x45, 0x47, 0x73, 0x26, 0x0A, 0x0D, 0x13,
0x17, 0x48, 0x42, 0x01, 0x40, 0x4D, 0x0C, 0x02, 0x69};//byte_403000[]的内容。注意我设置v4的长度为40。
int i;
v4[39]=4; //v4的值
for(i=38;i>=0;i--)//得到flag的值
{
v4[i]=v4[i]^v4[i+1];
printf("%c ",v4[i]);
}
printf("\n");
for(i=0;i<39;i++)//正序输出
{
printf("%c",v4[i]);
}
}
32.[MRCTF2020]Xor
IDA打开
然后f5,看关键代码。
开始写脚本。
#include<stdio.h>
int main(void)
{
int a[27]={'M', 'S', 'A', 'W', 'B', '~', 'F', 'X', 'Z', ':', 'J', ':', '`', 't', 'Q', 'J', '"', 'N', '@', ' ', 'b', 'p', 'd', 'd', '}', '8', 'g'};
int i;
for(i=0;i<27;i++)
{
a[i]=a[i]^i;
printf("%c",a[i]);
}
}
得到flag{@_R3@1ly_E2_R3verse!}
33.[GKCTF2020]BabyDriver
迷宫题,IDA打开
__int64 __fastcall sub_140001380(__int64 a1, __int64 a2)
{
__int64 v3; // rdi
__int64 v4; // rax
int v5; // ecx
__int16 *v6; // rsi
__int64 v7; // rbp
__int16 v8; // dx
char v9; // dl
const CHAR *v10; // rcx
if ( *(int *)(a2 + 48) >= 0 )
{
v3 = *(_QWORD *)(a2 + 24);
v4 = *(_QWORD *)(a2 + 56) >> 3;
if ( (_DWORD)v4 )
{
v5 = dword_1400030E4;
v6 = (__int16 *)(v3 + 2);
v7 = (unsigned int)v4;
while ( *(_WORD *)(v3 + 4) )
{
LABEL_28:
v6 += 6;
if ( !--v7 )
goto LABEL_29;
}
aO[v5] = 46;
v8 = *v6;
if ( *v6 == 23 )
{
if ( (v5 & 0xFFFFFFF0) != 0 )
{
v5 -= 16;//可以知道宽度为16
goto LABEL_21;
}
v5 += 208;
dword_1400030E4 = v5;
}
if ( v8 == 37 )
{
if ( (v5 & 0xFFFFFFF0) != 208 )
{
v5 += 16;
goto LABEL_21;
}
v5 -= 208;
dword_1400030E4 = v5;
}
if ( v8 == 36 )
{
if ( (v5 & 0xF) != 0 )
{
--v5;
goto LABEL_21;
}
v5 += 15;
dword_1400030E4 = v5;
}
if ( v8 != 38 )
goto LABEL_22;
if ( (v5 & 0xF) == 15 )
v5 -= 15;
else
++v5;
LABEL_21:
dword_1400030E4 = v5;
LABEL_22:
v9 = aO[v5];
if ( v9 == 42 )//等于42(*)就不行。
{
v10 = "failed!\n";
}
else
{
if ( v9 != 35 )//35(#),结尾
{
LABEL_27:
aO[v5] = 111;//111(o),开头
goto LABEL_28;
}
v10 = "success! flag is flag{md5(input)}\n";//最后得到的移动字符串还要进行md5加密。
}
dword_1400030E4 = 16;
DbgPrint(v10);
v5 = dword_1400030E4;
goto LABEL_27;
}
}
LABEL_29:
if ( *(_BYTE *)(a2 + 65) )
*(_BYTE *)(*(_QWORD *)(a2 + 184) + 3i64) |= 1u;
return *(unsigned int *)(a2 + 48);
}
大概分析一下就是a0[i]是迷宫是一个,14(行)*16(列)的迷宫,v5是到达的位置(也就是a0[i]中的i),然后35(#)结尾,111(o)开头。上下左右分别是23,37,36,38。
shift+e提取迷宫
开始走迷宫,得到38373737383837383737373838383737373838383838,然后进行md5加密,发现提交时不对,然后去看了wp,发现由于这是sys驱动文件,是由键盘过滤驱动获取键盘扫描码来控制上下左右,所以上下左右不是这几个值,而是IKJL。
键盘扫描码
最后得到flag{403950a6f64f7fc4b655dea696997851}
34.[MRCTF2020]hello_world_go
文件下载下来,直接用ida打开,选到hex view的窗口,Alt+t,搜索flag,就行了。至于为什么这么做,是因为shift+12出来的字符串太多了。也可以直接改文件后缀名为.txt,然后记事本打开搜索flag。
最后得到flag{hello_world_gogogo}
35.[WUSTCTF2020]level3
这是一道该base表的题。IDA打开
如果直接解的话,会是这个结果wa�f2�4�c�d4_is_p�e_stӲt�of_reverq�|,感觉有一点flag的影子,推测是变表的base编码。然后由于眼瞎找了半天才找到这个位置。
开始写脚本
import base64
import string
str1 = "d2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD=="
string1 = "TSRQPONMLKJIHGFEDCBAUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
print (base64.b64decode(str1.translate(str.maketrans(string1,string2))))
得到flag{Base64_is_the_start_of_reverse}
36.[FlareOn6]Overlong
文件下载下来,还有提示,说是flag被以一种方式藏了起来,直接打开,会弹出一个窗口。
然后拖到IDA里面,代码很少。
然后通过动调来该那个28,将他改成&unk_402008的长度B7-8。
然后改1C为AF。
得到flag
37.[WUSTCTF2020]Cr0ssfun
IDA打开
然后开始写脚本
#include<stdio.h>
int main(void)
{
puts(" _ _ _ _ _____ _____ _____ ");
puts("| | | | | | / ___|_ _| / ___| ");
puts("| | | | | | \\ `--. | | \\ `--. ___ ___ ");
puts("| |/\\| | | | |`--. \\ | | `--. \\/ _ \\/ __|");
puts("\\ /\\ / |_| /\\__/ / | | /\\__/ / __/ (__ ");
puts(" \\/ \\/ \\___/\\____/ \\_/ \\____/ \\___|\\___|");
int a1[42];
int i;
printf("\n");
a1[10] = 112;
a1[13] = 64;
a1[3] = 102;
a1[26] = 114;
a1[20] = 101;
a1[7] =48;
a1[16] =95;
a1[11] = 112;
a1[23] =101;
a1[30] = 117;
a1[0] = 119;
a1[6] = 50;
a1[22] = 115;
a1[31] = 110;
a1[12] = 95 ;
a1[15] = 100;
a1[8] = 123;
a1[18] = 51;
a1[28] = 95;
a1[21] = 114;
a1[2] = 116;
a1[9] = 99;
a1[32] = 125;
a1[19] = 118;
a1[5] = 48;
a1[14] = 110;
a1[4] = 50;
a1[17] = 114;
a1[29] = 102;
a1[17] = 114;
a1[24] = 95;
a1[1] = 99;
a1[25] = 64;
a1[27] = 101;
for(i=0;i<42;i++)
{
printf("%c",a1[i]);
}
}
得到flag{cpp_@nd_r3verse_@re_fun}
38.[ACTF新生赛2020]Oruga
这道题类似于迷宫题,只不过走的方式有些特别。
下面来分析如何走的迷宫。
得到flag{MEWEMEWJMEWJM}
39.[MRCTF2020]Transform
IDA打开,找到关键代码
开始写脚本
#include<stdio.h>
int main(void)
{
int a[33]={ 103, 121, 123, 127, 117, 43, 60, 82, 83, 121,
87, 94, 93, 66, 123, 45, 42, 102, 66, 126,
76, 87, 121, 65, 107, 126, 101, 60, 92, 69,
111, 98, 77};
int b[33]={9,0x0a,0x0f,0x17,7,0x18,0x0c,6,1,0x10,3,0x11,0x20,0x1d,0x0b,0x1e,0x1b,0x16,4,0x0d,0x13,0x14,0x15,2,0x19,5,0x1f,8,0x12,0x1a,0x1c,0x0e,0};
int flag[33];
int i,j;
for(i=0;i<33;i++)
{
flag[i]=a[i]^b[i];
printf("%c",flag[i]);
}
printf("\n");
for(i=0;i<33;i++)
{
for(j=0;j<33;j++)
{
if(b[j]==i)
printf("%c",flag[j]);
}
}
}
得到flag{Tr4nsp0sltiON_Clph3r_1s_3z}
40.Java逆向解密
.class文件,用jd-gui打开,得到代码
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Scanner;
public class Reverse
{
public static void main(String[] args)
{
Scanner s = new Scanner(System.in);
System.out.println("Please input the flag ��");
String str = s.next();
System.out.println("Your input is ��");
System.out.println(str);
char[] stringArr = str.toCharArray();
Encrypt(stringArr);
}
public static void Encrypt(char[] arr)
{
ArrayList<Integer> Resultlist = new ArrayList();
for (int i = 0; i < arr.length; i++)
{
int result = arr[i] + '@' ^ 0x20;
Resultlist.add(Integer.valueOf(result));
}
int[] KEY = { 180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65 };
ArrayList<Integer> KEYList = new ArrayList();
for (int j = 0; j < KEY.length; j++) {
KEYList.add(Integer.valueOf(KEY[j]));
}
System.out.println("Result:");
if (Resultlist.equals(KEYList)) {
System.out.println("Congratulations��");
} else {
System.err.println("Error��");
}
}
}
中间就只有一个result = arr[i] + ‘@’ ^ 0x20;的处理,开始写脚本
#include<stdio.h>
int main(void)
{
int a[]={180,136,137,147,191,137,147,191,148,136,133,191,134,140,129,135,191,65};
int i;
for(i=0;i<18;i++)
{
a[i]=a[i]-'@'^0x20;
printf("%c",a[i]);
}
}
得到flag{This_is_the_flag_!}
41.crackMe
IDA打开,找到主函数。
int wmain()
{
FILE *v0; // eax
FILE *v1; // eax
char v3; // [esp+3h] [ebp-405h]
char v4; // [esp+4h] [ebp-404h] BYREF
char v5[255]; // [esp+5h] [ebp-403h] BYREF
char Format; // [esp+104h] [ebp-304h] BYREF
char v7[255]; // [esp+105h] [ebp-303h] BYREF
char v8; // [esp+204h] [ebp-204h] BYREF
char v9[255]; // [esp+205h] [ebp-203h] BYREF
char v10; // [esp+304h] [ebp-104h] BYREF
char v11[255]; // [esp+305h] [ebp-103h] BYREF
printf("Come one! Crack Me~~~\n");
v10 = 0;
memset(v11, 0, sizeof(v11));
v8 = 0;
memset(v9, 0, sizeof(v9));
while ( 1 )
{
do
{
do
{
printf("user(6-16 letters or numbers):");
scanf("%s", &v10); //这里是输入的用户名
v0 = (FILE *)sub_4024BE();
fflush(v0);
}
while ( !(unsigned __int8)sub_401000(&v10) );
printf("password(6-16 letters or numbers):");
scanf("%s", &v8); //输入的密码,注意这里输入的是字符。
v1 = (FILE *)sub_4024BE();
fflush(v1);
}
while ( !(unsigned __int8)sub_401000(&v8) );
sub_401090(&v10); //对用户名进行了一个操作,这个函数没有什么用。
Format = 0;
memset(v7, 0, sizeof(v7));
v4 = 0;
memset(v5, 0, sizeof(v5));
v3 = ((int (__cdecl *)(char *, char *))loc_4011A0)(&Format, &v4); //这里要去花,最后得到Format=congratulation v4=Please try again
if ( sub_401830((int)&v10, &v8) )//这里是关于用户名和密码的一个函数
{
if ( v3 )//v3肯定会等于1。
break;
}
printf(&v4);
}
printf(&Format);
return 0;
}
分析sub_401830函数。
// v7----v15----v17,大概加密过程
bool __usercall sub_401830@<al>(int ebx0@<ebx>, int a1, const char *a2)
{
int v4; // [esp+18h] [ebp-22Ch]
signed int v5; // [esp+1Ch] [ebp-228h]
signed int v6; // [esp+28h] [ebp-21Ch]
unsigned int v7; // [esp+30h] [ebp-214h]
char v8; // [esp+36h] [ebp-20Eh]
char v9; // [esp+37h] [ebp-20Dh]
char v10; // [esp+38h] [ebp-20Ch]
unsigned __int8 v11; // [esp+39h] [ebp-20Bh]
unsigned __int8 v12; // [esp+3Ah] [ebp-20Ah]
char v13; // [esp+3Bh] [ebp-209h]
int v14; // [esp+3Ch] [ebp-208h]
char v15; // [esp+40h] [ebp-204h]
char v16; // [esp+41h] [ebp-203h]
char v17; // [esp+140h] [ebp-104h]
char v18; // [esp+141h] [ebp-103h]
v5 = 0;
v6 = 0;
v12 = 0;
v11 = 0;
v17 = 0;
memset(&v18, 0, 0xFFu);
v15 = 0;
memset(&v16, 0, 0xFFu);
v10 = 0;
v7 = 0;
v4 = 0;
while ( v7 < strlen(a2) )
{
if ( isdigit(a2[v7]) ) // 是不是10进制,若参数c为阿拉伯数字0~9,则返回非0值,否则返回0
{
v9 = a2[v7] - 48;//将输入的0~9字符,换为十进制的0~9
}
else if ( isxdigit(a2[v7]) ) // 是不是16进制0123456789abcdefABCDEF,是就返回非0
{
if ( *(_DWORD *)(*(_DWORD *)(__readfsdword(0x30u) + 24) + 12) != 2 )
a2[v7] = 34;
v9 = (a2[v7] | 0x20) - 87;//将输入的abcdefABCDEF,转换为十进制的10~15。
}
else
{
v9 = ((a2[v7] | 0x20) - 97) % 6 + 10;
}
v10 = v9 + 16 * v10;
if ( !((signed int)(v7 + 1) % 2) ) // 如果a[v7]中的v7是奇数就进入,这个的作用差不多就是,将输入的2个值,然后变成0~15的两个值,再通过v10 = v9 + 16 * v10;,就变成了一个值,给了*v15。例如1,a结果变换后就会变成一个值'1a'=26
{
*(&v15 + v4++) = v10;
ebx0 = v4;
v10 = 0;
}
++v7;
}
while ( v6 < 8 )//实际上从这里可以推测出密码长度为16。
{
v11 += byte_416050[++v12];
v13 = byte_416050[v12];
v8 = byte_416050[v11];
byte_416050[v11] = v13;
byte_416050[v12] = v8;
if ( *(_DWORD *)(__readfsdword(0x30u) + 104) & 0x70 )// 反调试,调试时if判断改为jmp。
v13 = v11 + v12;
*(&v17 + v6) = byte_416050[(unsigned __int8)(v8 + v13)] ^ *(&v15 + v5);//通过这个来得到v15,但是byte_416050[(unsigned __int8)(v8 + v13)]需要动调来获得。
if ( *(_DWORD *)(__readfsdword(0x30u) + 2) & 0xFF )// 反调试,调试时if判断改为jmp。
{
v11 = -83;
v12 = 43;
}
sub_401710((int)&v17, (const char *)a1, v6++);
v5 = v6;
if ( v6 >= (unsigned int)(&v15 + strlen(&v15) + 1 - &v16) )
v5 = 0;
}
v14 = 0;
sub_401470(ebx0, &v17, &v14); // 要让v14!=0,所以if里面的条件都要满足,只不过要注意里面有一个a1[5]是反调试, 得到v17=dbappsec
return v14 == 0xAB94;
}
现在已经知道了v17,现在通过动调来获得byte_416050[(unsigned __int8)(v8 + v13)],从而得到,&15。
图片中有个错误,应该是记录ecx的值
开始写脚本
#include<stdio.h>
int main(void)
{
int a[8]={0x2a,0xd7,0x92,0xe9,0x53,0xe2,0xc4,0xcd};//动调得到的byte_416050[(unsigned __int8)(v8 + v13)],也就是ecx。
int b[8]={'d', 'b', 'a', 'p', 'p', 's', 'e', 'c'};
int c[8];
int d[16];
int i,j,m,n;
for(i=0;i<8;i++)
{
c[i]=b[i]^a[i];
printf("%d ",c[i]);
}
for(i=0,j=0;i<8;i++,j+=2)
{
m=c[i]/16;
n=c[i]%16;
d[j]=m;
d[j+1]=n;
}
for(j=0;j<16;j++)
{
if(d[j]<10)
printf("%d",d[j]);
else
printf("%c",d[j]+87);
}
}
得到4eb5f3992391a1ae,再进行MD5加密得到flag{d2be2981b84f2a905669995873d6a36c}
42.刮开有奖
INT_PTR __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
const char *v4; // esi
const char *v5; // edi
int v7[2]; // [esp+8h] [ebp-20030h] BYREF
int v8; // [esp+10h] [ebp-20028h]
int v9; // [esp+14h] [ebp-20024h]
int v10; // [esp+18h] [ebp-20020h]
int v11; // [esp+1Ch] [ebp-2001Ch]
int v12; // [esp+20h] [ebp-20018h]
int v13; // [esp+24h] [ebp-20014h]
int v14; // [esp+28h] [ebp-20010h]
int v15; // [esp+2Ch] [ebp-2000Ch]
int v16; // [esp+30h] [ebp-20008h]
CHAR String[65536]; // [esp+34h] [ebp-20004h] BYREF
char v18[65536]; // [esp+10034h] [ebp-10004h] BYREF
if ( a2 == 272 )
return 1;
if ( a2 != 273 )
return 0;
if ( (_WORD)a3 == 1001 )
{
memset(String, 0, 0xFFFFu);
GetDlgItemTextA(hDlg, 1000, String, 0xFFFF);//这里的string是flag。
if ( strlen(String) == 8 )
{
v7[0] = 90;
v7[1] = 74;
v8 = 83;
v9 = 69;
v10 = 67;
v11 = 97;
v12 = 78;
v13 = 72;
v14 = 51;
v15 = 110;
v16 = 103;//上面10个数都是v7[]
sub_4010F0(v7, 0, 10);//对v7[]进行了处理。
memset(v18, 0, 0xFFFFu);
v18[0] = String[5];
v18[2] = String[7];
v18[1] = String[6];
v4 = (const char *)sub_401000(v18, strlen(v18));//sub_401000是长得比较丑的base64编码。对String[5],String[6],String[7]3个字母编码,得到了v4。
memset(v18, 0, 0xFFFFu);
v18[1] = String[3];
v18[0] = String[2];
v18[2] = String[4];
v5 = (const char *)sub_401000(v18, strlen(v18));对String[2],String[3],String[4]3个字母编码,得到了v5。
if ( String[0] == v7[0] + 34//得到sting[0]
&& String[1] == v10//得到sting[1]
&& 4 * String[2] - 141 == 3 * v8
&& String[3] / 4 == 2 * (v13 / 9)
&& !strcmp(v4, "ak1w")//通过这个来得到String[5],String[6],String[7]。'jMp'
&& !strcmp(v5, "V1Ax") )//通过这个来得到,String[2],String[3],String[4]。'WP1'
{
MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
}
}
return 0;
}
if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
return 0;
EndDialog(hDlg, (unsigned __int16)a3);
return 1;
}
现在看sub_4010F0对v7[]进行了什么操作
int __cdecl sub_4010F0(int a1, int a2, int a3)
{
int result; // eax
int i; // esi
int v5; // ecx
int v6; // edx
result = a3;
for ( i = a2; i <= a3; a2 = i )
{
v5 = 4 * i;
v6 = *(_DWORD *)(4 * i + a1);
if ( a2 < result && i < result )
{
do
{
if ( v6 > *(_DWORD *)(a1 + 4 * result) )
{
if ( i >= result )
break;
++i;
*(_DWORD *)(v5 + a1) = *(_DWORD *)(a1 + 4 * result);
if ( i >= result )
break;
while ( *(_DWORD *)(a1 + 4 * i) <= v6 )
{
if ( ++i >= result )
goto LABEL_13;
}
if ( i >= result )
break;
v5 = 4 * i;
*(_DWORD *)(a1 + 4 * result) = *(_DWORD *)(4 * i + a1);
}
--result;
}
while ( i < result );
}
LABEL_13:
*(_DWORD *)(a1 + 4 * result) = v6;
sub_4010F0(a1, a2, i - 1);//这里还有个递归
result = a3;
++i;
}
return result;
}
由于所有值都知道,所以可以直接复制下来,得到改变后的v7[]。需要注意的是,里面出现的(_DWORD *)都删了,特别是4 *也要删除,原因是这些变量都是int型(占了4个字节),而这部分在IDA里面看汇编代码
开始写脚本得到v7[]
#include<stdio.h>
int fun(int *a1,int a2,int a3)
{
int result; // eax
int i; // esi
int v5; // ecx
int v6; // edx
result = a3;
for ( i = a2; i <= a3; a2 = i )
{
v5 =i;
v6 = *( i + a1);
if ( a2 < result && i < result )
{
do
{
if ( v6 > *(a1 + result) )
{
if ( i >= result )
break;
++i;
*(v5 + a1) = *(a1 +result);
if ( i >= result )
break;
while ( *(a1 + i) <= v6 )
{
if ( ++i >= result )
goto LABEL_13;
}
if ( i >= result )
break;
v5 = i;
*(a1 + result) = *(i + a1);
}
--result;
}
while ( i < result );
}
LABEL_13:
*(a1 +result) = v6;
fun(a1, a2, i - 1);
result = a3;
++i;
}
return result;
}
int main(void)
{
int v7[11]={90,74,83,69,67,97,78,72,51,110,103};
int i;
fun(v7,0,10);
for(i=0;i<11;i++)
{
printf("%d ",v7[i]);
}
}
得到v7=51 67 69 72 74 78 83 90 97 103 110;从而得到string[0]=’U’,string[1]=’J’
得到flag{UJjMpWP1}
43.[ACTF新生赛2020]rome
脚本
code=list('Qsw3sj_lz4_Ujw@l')
flag=''
i=0
while(i<=15):
for k in range(0,127):
z=k
if(64 < k <= 90):
k = (k - 51) % 26 + 65
if(96<k<=122):
k = (k - 79) % 26 + 97
if(chr(k)==code[i]):
flag += chr(z)
i=i+1
print(flag)
Crypto方向
1.[BJDCTF2020]这是base??
下载下来得到一个txt文件,发现是一个换表的base64编码的密文。写出脚本如下
#include<stdio.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>
int main(void)
{
char str1[100]="FlZNfnF6Qol6e9w17WwQQoGYBQCgIkGTa9w3IQKw";
char str2[100];
int table[]={0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,62,0,0,0,
63,42,43,44,45,46,47,48,
19,20,21,0,0,0,0,0,0,0,11,
12,13,14,15,16,17,18,24,0,1,2,3,
4,5,22,23,49,50,51,8,9,
38,39,40,41,0,0,0,0,0,0,25,
26,27,28,29,30,31,32,33,34,
35,36,37,52,53,54,55,56,57,
58,59,60,61,6,7,10
};
int len1;
int len2;
int i,j;
len1=strlen(str1);
if(strstr(str1,"=="))
{
len2=(len1/4)*3-2;
}
else if(strstr(str1,"="))
{
len2=(len1/4)*3-1;
}
else
{
len2=(len1/4)*3;
}
for(i=0,j=0;i<len1-2;i+=4,j+=3)
{
str2[j]=((table[str1[i]])<<2)|((table[str1[i+1]])>>4);
str2[j+1]=(table[str1[i+1]]<<4)|((table[str1[i+2]])>>2);
str2[j+2]=((table[str1[i+2]])<<6)|(table[str1[i+3]]);
}
// printf("%d",len2);
for(i=0;i<len2;i++)
{
printf("%c",str2[i]);
}
return 0;
}
得到flag{D0_Y0u_kNoW_Th1s_b4se_map}
网上找的Python脚本如下
import base64
dict={0: 'J', 1: 'K', 2: 'L', 3: 'M', 4: 'N', 5: 'O', 6: 'x', 7: 'y', 8: 'U', 9: 'V', 10: 'z', 11: 'A', 12: 'B', 13: 'C', 14: 'D', 15: 'E', 16: 'F', 17: 'G', 18: 'H', 19: '7', 20: '8', 21: '9', 22: 'P', 23: 'Q', 24: 'I', 25: 'a', 26: 'b', 27: 'c', 28: 'd', 29: 'e', 30: 'f', 31: 'g', 32: 'h',33: 'i', 34: 'j', 35: 'k', 36: 'l', 37: 'm', 38: 'W', 39: 'X', 40: 'Y', 41: 'Z', 42: '0', 43: '1', 44: '2', 45: '3', 46: '4', 47: '5', 48: '6', 49: 'R', 50: 'S', 51: 'T', 52: 'n', 53: 'o', 54: 'p', 55: 'q', 56: 'r', 57: 's', 58: 't', 59: 'u', 60: 'v', 61: 'w', 62: '+', 63: '/', 64: '='}
base64_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P','Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f','g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v','w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/']
cipher='FlZNfnF6Qol6e9w17WwQQoGYBQCgIkGTa9w3IQKw'
res=''
for i in range(len(cipher)):
for j in range(64):
if(dict[j]==cipher[i]):
res+=base64_list[j]
flag=base64.b64decode(res)
print(flag)