[ACTF新生赛2020]rome
查壳
![](https://img-blog.csdnimg.cn/img_convert/684baa9eb575d71d1bb711c6930143d9.png)
ida打开,通过字符窗口(按shift+F12)找可疑字符
int func()
{
int result; // eax
int v1; // [esp+14h] [ebp-44h]
int v2; // [esp+18h] [ebp-40h]
int v3; // [esp+1Ch] [ebp-3Ch]
int v4; // [esp+20h] [ebp-38h]
unsigned __int8 v5; // [esp+24h] [ebp-34h]
unsigned __int8 v6; // [esp+25h] [ebp-33h]
unsigned __int8 v7; // [esp+26h] [ebp-32h]
unsigned __int8 v8; // [esp+27h] [ebp-31h]
unsigned __int8 v9; // [esp+28h] [ebp-30h]
int v10; // [esp+29h] [ebp-2Fh]
int v11; // [esp+2Dh] [ebp-2Bh]
int v12; // [esp+31h] [ebp-27h]
int v13; // [esp+35h] [ebp-23h]
unsigned __int8 v14; // [esp+39h] [ebp-1Fh]
char v15; // [esp+3Bh] [ebp-1Dh]
char v16; // [esp+3Ch] [ebp-1Ch]
char v17; // [esp+3Dh] [ebp-1Bh]
char v18; // [esp+3Eh] [ebp-1Ah]
char v19; // [esp+3Fh] [ebp-19h]
char v20; // [esp+40h] [ebp-18h]
char v21; // [esp+41h] [ebp-17h]
char v22; // [esp+42h] [ebp-16h]
char v23; // [esp+43h] [ebp-15h]
char v24; // [esp+44h] [ebp-14h]
char v25; // [esp+45h] [ebp-13h]
char v26; // [esp+46h] [ebp-12h]
char v27; // [esp+47h] [ebp-11h]
char v28; // [esp+48h] [ebp-10h]
char v29; // [esp+49h] [ebp-Fh]
char v30; // [esp+4Ah] [ebp-Eh]
char v31; // [esp+4Bh] [ebp-Dh]
int i; // [esp+4Ch] [ebp-Ch]
v15 = 81;
v16 = 115;
v17 = 119;
v18 = 51;
v19 = 115;
v20 = 106;
v21 = 95;
v22 = 108;
v23 = 122;
v24 = 52;
v25 = 95;
v26 = 85;
v27 = 106;
v28 = 119;
v29 = 64;
v30 = 108;
v31 = 0;
printf("Please input:");
scanf("%s", &v5);
result = v5;
if ( v5 == 65 )
{
result = v6;
if ( v6 == 67 )
{
result = v7;
if ( v7 == 84 )
{
result = v8;
if ( v8 == 70 )
{
result = v9;
if ( v9 == 123 )
{
result = v14;
if ( v14 == 125 )
{
v1 = v10;
v2 = v11;
v3 = v12;
v4 = v13;
for ( i = 0; i <= 15; ++i )
{
if ( *((_BYTE *)&v1 + i) > 64 && *((_BYTE *)&v1 + i) <= 90 )// 对大写的字母进行下面的加密
*((_BYTE *)&v1 + i) = (*((char *)&v1 + i) - 51) % 26 + 65;
if ( *((_BYTE *)&v1 + i) > 96 && *((_BYTE *)&v1 + i) <= 122 )// 对小写的字母进行下面的加密
*((_BYTE *)&v1 + i) = (*((char *)&v1 + i) - 79) % 26 + 97;
}
for ( i = 0; i <= 15; ++i )
{
result = (unsigned __int8)*(&v15 + i);
if ( *((_BYTE *)&v1 + i) != (_BYTE)result )
return result;
}
result = printf("You are correct!");
}
}
}
}
}
}
return result;
}
在写脚本的时候,一定要注意取余运算,这个运算在逆向的时候比较麻烦,比较好的方法是枚举,
观察这个取余运算的数,65(A的ASCII码)、97(a的ASCII码)、26,不难想到,实质上就是对字母进行偏移;然后再看51和79,都是26的倍数加或减1;也可以直接打开这个括号,再进行运算。那么这个取余运算的作用也比较容易理解,下面是代码:
decry = "Qsw3sj_lz4_Ujw@l"
v = list(decry)
for i in range(16):
if ord(v[i])>64 and ord(v[i])<=90:
v[i] = chr(ord(v[i])-65+51) if (ord(v[i])-65+51) > 64 else chr(ord(v[i])-65+26+51)
if ord(v[i]) > 96 and ord(v[i]) <= 122:
v[i] = chr(ord(v[i]) - 97 + 79) if (ord(v[i])-97+79) > 96 else chr(ord(v[i])-97+26+79)
print("".join(v))
flag{Cae3ar_th4_Gre@t}
[FlareOn4]login
下载有两个文件,文本文件没什么用,看网页
![](https://img-blog.csdnimg.cn/img_convert/a68e5f18fdb00940457f0dc9d212d38b.png)
源代码里有用的是下面这段代码
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");
}
}
replace的第一个参数,/[a-zA-Z]/g 这部分是正则表达式,相关内容可参考https://www.runoob.com/regexp/regexp-syntax.html
replace的第二个参数,是一个函数,也就是将输入的字符串的所有字符,进行这个函数的加密
加密后的字符串是"PyvragFvqrYbtvafNerRnfl@syner-ba.pbz"
.charCodeAt(i) 方法是返回字符串的第i个字符的Unicode编码(i从0算起)
.fromCharCode() 方法是将一个Unicode编码转换成对应的字符
实质上就是对字符串进行偏移,偏移量为固定的13。。ROT13加密
flag{ClientSideLoginsAreEasy@flare-on.com}
CrackRTF
哈希
首先了解一下关于哈希(Hash):
哈希(Hash)是将任意长度的数据映射到固定长度的数据的过程,被映射后的数据称为哈希值(Hash Value)。哈希函数是实现哈希的算法,它可以将输入数据(也称为消息)转换为哈希值。
哈希值具有以下特点:
固定长度:无论输入数据的大小,哈希值的长度都是固定的。
唯一性:哈希函数将不同的输入数据映射到不同的哈希值,因此不同的输入数据生成的哈希值是唯一的。
散列性:哈希函数应该具有很高的散列性,即对于微小的输入数据变化,生成的哈希值应该有很大的差异,从而使哈希值更难以被预测或攻击者更难以针对性地构造相同的哈希值。
不可逆性:哈希函数应该是单向的,即无法通过哈希值还原出原始数据。
哈希值在密码学中广泛应用,例如数字签名、消息认证和数据完整性验证。在网络安全中,哈希函数也被用于验证文件的完整性和防止篡改,例如文件校验和和数字指纹。
一些常用的哈希函数包括:MD5、SHA-1、SHA-2 和 SHA-3。需要注意的是,随着计算机计算能力的不断提高,一些早期的哈希函数,如 MD5 和 SHA-1,已经被认为不再安全,建议使用更安全的哈希函数。
不同哈希函数简介
MD5:输出长度为128位的散列值。由于其输出长度较短,MD5易于被暴力破解。因此,MD5已经不再被推荐用于安全加密。
SHA-1:输出长度为160位的散列值。SHA-1的安全性也被认为不足,容易受到碰撞攻击。因此,SHA-2和SHA-3等更强大的哈希函数已被推荐用于安全加密。
SHA-2:SHA-2系列包括SHA-224、SHA-256、SHA-384和SHA-512,分别输出长度为224、256、384和512位的散列值。SHA-2相对于MD5和SHA-1更加安全,被广泛使用于安全加密领域。
SHA-3:SHA-3是NIST于2012年发布的新一代哈希函数标准。SHA-3与SHA-2不同的是,它采用了基于置换的架构,具有更好的安全性和效率。
BLAKE2:BLAKE2是一种高速哈希函数,输出长度可以从1到512位不等。它比SHA-3和SHA-2更快,也被广泛用于数据完整性校验和密码学协议等领域。
这里的散列值相当于一个字节,下面是不同进制的单个字符再计算机里所占内存大小:
二进制:每个二进制字符(0或1)占据1比特位,8个二进制字符组成一个字节。
八进制:每个八进制字符(0-7)占据3比特位,3个八进制字符组成一个字节。
十进制:每个十进制字符(0-9)占据1个字节(8个比特位)。
十六进制:每个十六进制字符(0-9,A-F)占据4比特位,2个十六进制字符组成一个字节。
题目
查壳,32位无壳,IDA32打开,找到main_0,有以下代码
int __cdecl main_0()
{
DWORD v0; // eax
DWORD v1; // eax
CHAR String; // [esp+4Ch] [ebp-310h]
int v4; // [esp+150h] [ebp-20Ch]
CHAR String1; // [esp+154h] [ebp-208h]
BYTE pbData; // [esp+258h] [ebp-104h]
memset(&pbData, 0, 0x104u);
memset(&String1, 0, 0x104u);
v4 = 0;
printf("pls input the first passwd(1): ");
scanf("%s", &pbData);
if ( strlen((const char *)&pbData) != 6 )
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
v4 = atoi((const char *)&pbData);
if ( v4 < 100000 )
ExitProcess(0);
strcat((char *)&pbData, "@DBApp");
v0 = strlen((const char *)&pbData);
sub_40100A(&pbData, v0, &String1);
if ( !_strcmpi(&String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )
{
printf("continue...\n\n");
printf("pls input the first passwd(2): ");
memset(&String, 0, 0x104u);
scanf("%s", &String);
if ( strlen(&String) != 6 )
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
strcat(&String, (const char *)&pbData);
memset(&String1, 0, 0x104u);
v1 = strlen(&String);
sub_401019((BYTE *)&String, v1, &String1);
if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", &String1) )
{
if ( !sub_40100F(&String) )
{
printf("Error!!\n");
ExitProcess(0);
}
printf("bye ~~\n");
}
}
return 0;
}
26行的一串,是40位数字,并且是16进制,那么就是SHA-1加密了
要求两位6位字符的passwd,先输入第一段字符,在其后面加上@DBApp,再经过sub_40100A加密;再输入第二段字符,sub_401019加密,将其拼接在第一段字符的前面;然后整体再加密一次
这里的atoi()函数用于将字符串转换成整型数
memset()函数:void *memset(void *ptr, int value, size_t num),其中,ptr是指向内存区域的指针,value是要设置的值,num是要设置的字节数。用于将指定内存区域的每个字节都设置成指定的值。
其中,第一段和第二段加密后的字符给出了
那么先来爆破第一段:
import hashlib
flag = "@DBApp"
for i in range(100000, 1000000):
s = str(i)+flag
x = hashlib.sha1(s.encode())
cnt = x.hexdigest()
if "6e32d0943418c2c" in cnt:
print(cnt)
print(str(i)+flag)
由于SHA-1哈希函数只接受字节序列作为输入,所以需要.encode(),encode()默认为UTF-8
结果123321@DBApp
第二次加密,给出的字符长度有变,是32位,可能是MD5加密,往后看,有个sub_40100F函数,点进去,看到这种字符串,这个函数肯定有用,看起来像从AAA这个文件取出什么东西
![](https://img-blog.csdnimg.cn/img_convert/9fab9d26ac742624866f5a53b9643495.png)
进入这个函数,是个异或运算
![](https://img-blog.csdnimg.cn/img_convert/ffdb11eb1b99d16f7cd7256282362b43.png)
这里附上一个工具Resource Hacker的官网http://www.angusj.com/resourcehacker/#download,这个工具可以直接查看文件中的资源。
![](https://img-blog.csdnimg.cn/img_convert/a5d4014d14b83f59ec0c92bc8cdd8f43.png)
文件.rtf的文件头固定为{\rtf1\ansi\ansicpg936\deff0\......,取前六位{\rtf1。
所以{\rtf1与AAA中前六个数据异或就行了
![](https://img-blog.csdnimg.cn/img_convert/b94480c95f90ba0c11311d6a12932a78.png)
结果~!3a@0
写到这突然不知道接下来要干什么了,这道题对小白来说也算难的了吧
那就运行一下exe,两次输入,最后发现dbapp.rtf,
flag{N0_M0re_Free_Bugs}