SimpleRev
查壳
放ida里面
这里给出的src和v9需要判断一下是大端还是小端
用指针法判断出是小端,故得到的字符串要反着写
后面半段是加密流程, 直接把全部字母尝试一遍即可
flag{KLDQCUDFZO}
Luck-guy
查壳
64位无壳
跟踪进patch_me函数,
继续跟进
跟踪进f1得到flag第一段,
在case 4中s后面的是小端,在读写时要反着写;跟进f2发现f2什么也没有;把s加到f2中,即此时f2里面就是s;在case 5里,对f2进行了一段加密;根据case1里flag=s+f1+f2,s先被初始化为0了,所以flag就是f1和·f2两段。编写函数求出f2加密后的结果:
把f1和f2拼接起来就是flag:GXY{do_not_hate_me},由于Buuctf里面要求用flag{}形式,故把GXY换成flag就行,即flag{do_not_hate_me}
JustRe
查壳
32位,无壳,用ida打开
这里的WinMain函数是程序的入口点函数,在这个函数里面找不到有用的信息,按shift+F12去字符串里面找
在最后一行发现了一个以flag形式的字符串,跟踪进去后交叉引用,打开另一个函数
这里的sprintf函数承担着将字符串复制储存到String数组里,然后用19999和0替换字符串中的%d的作用。SetwindowTextA是起到改变窗口标题栏的文本内容的作用,即作用于运行后的窗口
所以它不会改变String,因此用19999和0替换字符串里的%d后的结果就是flag,即flag{1999902069a45792d233ac}
刮开有奖
查壳
32位,无壳,用ida打开
打开DialogFunc函数
跟进sub_401000函数,
跟进这个byte_407830
是个base64表,由此可知,这个sub函数对数据进行了base64加密。把需要和v4及v5比较的字符串用工具进行base64解密,然后按string[]数组的顺序排列得到“WP1jMp”这就是flag的后六位。因为前面的赋值时连续赋值,所以他们的地址是连续的,跟踪进sub_4010F0函数,这里对前面赋值的数进行了一系列操作。
函数很长,把这个函数复制到编译软件里,稍微修改使之能运行
#include<stdio.h>
#include<string.h>
int __cdecl sub_C110F0(char* 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 = a1[i];
if ( a2 < result && i < result )
{
do
{
if ( v6 > a1 [result] )
{
if ( i >= result )
break;
++i;
a1[v5] = a1[result];
if ( i >= result )
break;
while ( a1[i] <= v6 )
{
if ( ++i >= result )
goto LABEL_13;
}
if ( i >= result )
break;
v5 = i;
a1[result] = a1[i];
}
--result;
}
while ( i < result );
}
LABEL_13:
a1[result] = v6;
sub_C110F0(a1, a2, i - 1);
result = a3;
++i;
}
return result;
}
int main()
{
char v7[]="ZJSECaNH3ng";
sub_C110F0(v7, 0, 10);
//printf("%s",v7);
for(int i=0;i<=9;i++)
{
printf("%d ",v7[i]);
}
return 0;
}
运行得到
我们只需要用到v7[0]和v10,即第一个和第5个,
根据条件,可以得知,String[0]=51+34=85=U,String[1]=74=J。所以flag{UJWP1jMp}。
easyre
查壳
32位有壳,进行去壳操作
去壳结束后用ida打开,
由if里面的判断语句可知,flag是以“ACTF{}”形式的,flag就在括号里面,qmemcpy函数就是把后面的字符串复制到v4里面,因为循环是12次的,猜测flag就是12位,
由以上可知v4[i] = _data_start__[v5[i] - 1],也就是说,与v4[i]相同的字符在data数组中的位置+1=v5[i]。编写代码如下:
#include<stdio.h>
#include<string.h>
int main()
{
char s[]="~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(\'&%$# !\"";
char v[]="*F'\"N,\"(I?+@";
for(int i=0;i<strlen(v);i++)
{
for(int j=0;j<strlen(s);j++)
{
if(v[i]==s[j])
{
printf("%c",j+1);
break;
}
}
}
return 0;
}
flag{U9X_1S_W6@T?}
pyre
下载后发现是一个pyc文件,将其与pycdc.exe文件放同一个文件夹,在终端中输入“pycdc.exe attachment.pyc”,得到如下伪代码:
D:\逆向\python逆向>pycdc.exe attachment.pyc
# Source Generated with Decompyle++
# File: attachment.pyc (Python 2.7)
print 'Welcome to Re World!'
print 'Your input1 is your flag~'
l = len(input1)
for i in range(l):
num = ((input1[i] + i) % 128 + 128) % 128
code += num
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']
D:\逆向\python逆向>
flag进行了两次加密后等于code,在异或操作中,唯一没有改变的是code的最后一位,所以反向解密,反向异或后得到的数组直接进行暴力解法,由于ASCII表只有0~127这些数字,所以直接遍历一遍找flag:
#include<stdio.h>
#include<string.h>
int main()
{
char s[]={'\x1f','\x12','\x1d','(','0','4','\x01','\x06','\x14','4',',','\x1b','U','?','o','6','*',':','\x01','D',';','%','\x13'};
for(int i=strlen(s)-2;i>=0;i--)
{
s[i] = s[i] ^ s[i + 1];
}
for(int i=0;i<=strlen(s)-1;i++)
{
for(int j=0;j<=127;j++)
{
if(s[i] == ((j + i) % 128 + 128) % 128)
{
printf("%c",j);
}
}
}
return 0;
}
把GWHT换成flag就是flag了,即flag{Just_Re_1s_Ha66y!}
rome
查壳
32位无壳,用ida打开
跟进func函数:
将v2,v3等数转化为字符,得到了“ACTF{}",是flag的形式,可知flag就是包在括号里的内容,因为括号里的内容赋值给了v1,所以v1就是flag。先分析一下加密函数:
这里*(_DWORD *)&v12[17]就是一个变量,类似循环里的i,所以*((char *)v1 + *(_DWORD *)&v12[17])就是指v1[i],将变量名修改后就更容易看明白加密过程了:
i = 0;
while ( i <= 15 )
{
if ( v1[i] > 64 && v1[i] <= 90 )
v1[i] = (v1[i] - 51) % 26 + 65;
if ( v1[i] > 96 && v1[i] <= 122 )
v1[i] = (v1[i] - 79) % 26 + 97;
++i;
}
i = 0;
while ( i <= 15 )
{
result = v12[i];
if ( v1[i] != result )
return result;
++i;
}
return printf("You are correct!");
因为加密过程进行了取余处理,不好反向推,所以选择直接遍历ASCII为0~127的字符来筛选出符合条件的字符,编写代码如下:
#include<stdio.h>
#include<string.h>
int main()
{
char s[]="Qsw3sj_lz4_Ujw@l" ;
int x;
for(int i=0;i<=15;i++)
{
for(int j=0;j<=127;j++)
{
x=j;
if(j>64&&j<=90)
x=(x-51)%26+65;
if(j>96&&j<=122)
x=(x-79)%26+97;
if(x==s[i])
{
printf("%c",j);
}
}
}
return 0;
}
flag{Cae3ar_th4_Gre@t}
login
下载后打开html文件,跳转到了一个网页,按F12获得其源代码
<!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>
对flag里面的字符进行了替换操作,替换成的是“(c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26”得到的结果,这一条指令,其实就是分为4种情况:
if(s[i]<='M'&&s[i]>='A')
printf("%c",s[i]+13);
else if(s[i]<='Z'&&s[i]>='N')
printf("%c",s[i]-13);
else if(s[i]<='m'&&s[i]>='a')
printf("%c",s[i]+13);
else if(s[i]<='z'&&s[i]>='n')
printf("%c",s[i]-13);
这一段意思是一个从A~Z(或a~z)的字母,如果它在前半段(即A~M),就把它替换为位次是它后13位的字母;如果在后半段(即N~Z),就把它替换为它前13为的字母。由于英文字母是26个,所以字母往前13位和往后13位对应的字母都是一样的,也就是说,每两个字母一一对应,A和N对应,B和O对应·····M和Z对应。所以这一段加密就是把A换成N,把N换成A,把B换成O,把O换成B······。要解密的话,只需用和加密相同的代码做脚本
#include<stdio.h>
#include<string.h>
int main()
{
char s[]="PyvragFvqrYbtvafNerRnfl@syner-ba.pbz";
for(int i=0;i<strlen(s);i++)
{
if(s[i]<='M'&&s[i]>='A')
printf("%c",s[i]+13);
else if(s[i]<='Z'&&s[i]>='N')
printf("%c",s[i]-13);
else if(s[i]<='m'&&s[i]>='a')
printf("%c",s[i]+13);
else if(s[i]<='z'&&s[i]>='n')
printf("%c",s[i]-13);
else printf("%c",s[i]);
}
return 0;
}
flag{ClientSideLoginsAreEasy@flare-on.com}
level 1
下载后有两个文件,一个文件叫output,里面是一串数字,对另一个文件查壳
64位,用ida打开
fopen函数是打开文件,fread是读取文件后存放数据到ptr数组里,fclose是关闭文件,所以flag就在ptr数组里面,后面是一堆加密和输出,其中i&1是判断奇偶数:1换为二进制是0000 0001,进行与运算无论最后一位前面是1还是零,与运算后一定是0,所以只需要看最后一位,如果最后一位是0,那么i&1就是0,如果最后一位是1,那么i&1结果是1.如果是奇数,那么最后一位一定是1,而偶数则是0.所以它可以判断奇偶性。
对于ptr这个数组,是20位的,但是循环从ptr[1]开始,也就是ptr[0]是什么不影响后面的输出,从文件名是output可知,输出的结果是文件里面的数字,编写脚本如下
#include<stdio.h>
#include<string.h>
int main()
{
int s[]={198,232,816,200,1536,300,6144,984,51200,570,92160,1200,565248,756,1474560,800,6291456,1782 ,65536000 };
for(int i=0;i<=18;i++)
{
if ( ((i+1) & 1) != 0 )
printf("%c", (s[i] >> i+1));
else
printf("%c", s[i]/(i+1));
}
}
至于为什么是i+1,这是因为ptr[0]没有参与输出,所以s[0]对标的是ptr[1],对应的是i=1,而不是0
运行这段代码得到flag{d9-dE6-20c}