目录
[GWCTF 2019]pyre
是pyc文件,可以用反编译工具,我这里是在线反编译,地址。
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: 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",
]
直接写脚本:
code = ["\x1f", "\x12", "\x1d", "(", "0", "4", "\x01", "\x06", "\x14", "4", ",", "\x1b", "U",
"?", "o", "6", "*", ":", "\x01", "D", ";", "%", "\x13"]
le = len(code)
for i in range(-2, -le, -1):
code[i] = chr(ord(code[i]) ^ ord(code[i + 1]))
# print(code)
flag = ''
for i in range(le):
for j in range(128):
if((j + i) % 128 == ord(code[i])):
flag += chr(j)
print(flag)
"""
第二个循环的另一种方式
for i in range(le):
code[i] = chr((ord(code[i]) - i) % 128)
flag += code[i]
"""
[ACTF新生赛2020]easyre
upx壳
脱掉后
int __cdecl main(int argc, const char **argv, const char **envp)
{
_BYTE v4[12]; // [esp+12h] [ebp-2Eh] BYREF
_DWORD v5[3]; // [esp+1Eh] [ebp-22h]
_BYTE v6[5]; // [esp+2Ah] [ebp-16h] BYREF
int v7; // [esp+2Fh] [ebp-11h]
int v8; // [esp+33h] [ebp-Dh]
int v9; // [esp+37h] [ebp-9h]
char v10; // [esp+3Bh] [ebp-5h]
int i; // [esp+3Ch] [ebp-4h]
__main();
qmemcpy(v4, "*F'\"N,\"(I?+@", sizeof(v4));
printf("Please input:");
scanf("%s", v6);
if ( v6[0] != 'A' || v6[1] != 'C' || v6[2] != 'T' || v6[3] != 'F' || v6[4] != '{' || v10 != '}' )
return 0;
v5[0] = v7;
v5[1] = v8;
v5[2] = v9;
for ( i = 0; i <= 11; ++i )
{
if ( v4[i] != _data_start__[*((char *)v5 + i) - 1] )
return 0;
}
printf("You are correct!");
return 0;
}
v6 = "ACTF{}",v5为括号中的内容,至于为什么是,我认为是靠猜和经验,伪代码本身不难。_data_start是字符串(这样伪代码才合理),其中有几个十六进制数要转为字符
脚本
v4 = "*F'\"N,\"(I?+@"
_data_start_ = "~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(\'&%$# !\""
v5 = ''
for i in v:
v5 += chr(_data_start_.find(i) + 1) # find字符查找,可规定范围
# print(v5)
print(v5)
[ACTF新生赛2020]rome
关键在func()函数,v1为flag
int func()
{
int result; // eax
int v1[4]; // [esp+14h] [ebp-44h]
unsigned __int8 v2; // [esp+24h] [ebp-34h] BYREF
unsigned __int8 v3; // [esp+25h] [ebp-33h]
unsigned __int8 v4; // [esp+26h] [ebp-32h]
unsigned __int8 v5; // [esp+27h] [ebp-31h]
unsigned __int8 v6; // [esp+28h] [ebp-30h]
int v7; // [esp+29h] [ebp-2Fh]
int v8; // [esp+2Dh] [ebp-2Bh]
int v9; // [esp+31h] [ebp-27h]
int v10; // [esp+35h] [ebp-23h]
unsigned __int8 v11; // [esp+39h] [ebp-1Fh]
char v12[29]; // [esp+3Bh] [ebp-1Dh] BYREF
strcpy(v12, "Qsw3sj_lz4_Ujw@l");
printf("Please input:");
scanf("%s", &v2);
result = v2;
if ( v2 == 'A' )
{
result = v3;
if ( v3 == 'C' )
{
result = v4;
if ( v4 == 'T' )
{
result = v5;
if ( v5 == 'F' )
{
result = v6;
if ( v6 == '{' )
{
result = v11;
if ( v11 == '}' ) // 有十六位
{
v1[0] = v7;
v1[1] = v8;
v1[2] = v9;
v1[3] = v10;
*(_DWORD *)&v12[17] = 0; // 初始化,可以看作取i=0
while ( *(int *)&v12[17] <= 15 ) //可以看作i<=15
{ //可以看作v1[i]>64&&v1[i]<=90
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 64 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 90 )
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 51) % 26 + 65;
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 96 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 122 )
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 79) % 26 + 97;
++*(_DWORD *)&v12[17];
}
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )//这里是在验证加密后v1是否
//等于"Qsw3sj_lz4_Ujw@l"
{
result = (unsigned __int8)v12[*(_DWORD *)&v12[17]];
if ( *((_BYTE *)v1 + *(_DWORD *)&v12[17]) != (_BYTE)result )
return result;
++*(_DWORD *)&v12[17];
}
result = printf("You are correct!");
}
}
}
}
}
}
return result;
}
python脚本
v1 = "Qsw3sj_lz4_Ujw@l"
flag = ''
for i in v1:
if(ord(i) >= 97 and ord(i) < 123):
for j in range(97, 123):
if((j - 79) % 26 + 97 == ord(i)):
flag += chr(j)
elif(ord(i) >= 65 and ord(i) < 91):
for j in range(65, 91):
if((j - 51) % 26 + 65 == ord(i)):
flag += chr(j)
else:
flag += i
print("flag{" + flag + "}")
CrackRTF
运行一下,对程序有了一个初步认识。查壳发现是32位无壳,直接放进IDA。查看String window跟进字符串 pls input the first passwd(1):反汇编得到伪代码。
函数先后共获取了两段字符串,第一段经过检验后与@DBApp连接到一起并进入函数sub_40100A进行SHA加密再验证结果,然后与第二段字符串连接进入函数sub_401019进行MD5加密再验证结果,这就是函数的大致流程了。介绍一种取巧的方法,直接把用来验证MD5加密的值用在线MD5解密网站解出就可以得到所求的字符串,虽然MD5理论上不可解密,但终究是有方法的。运行程序先后输入123321与~!3a@0可得到一个rtf文件,flag就在里面
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
DWORD v3; // eax
DWORD v4; // eax
char Str[260]; // [esp+4Ch] [ebp-310h] BYREF
int v7; // [esp+150h] [ebp-20Ch]
char String1[260]; // [esp+154h] [ebp-208h] BYREF
char Destination[260]; // [esp+258h] [ebp-104h] BYREF
memset(Destination, 0, sizeof(Destination)); // 初始化
memset(String1, 0, sizeof(String1));
v7 = 0;
printf("pls input the first passwd(1): ");
scanf("%s", Destination); //
if ( strlen(Destination) != 6 )
{
printf("Must be 6 characters!\n"); // 6位
ExitProcess(0);
}
v7 = atoi(Destination); // 把参数 str 所指向的字符串转换为一个整数
if ( v7 < 100000 )
ExitProcess(0);
strcat(Destination, "@DBApp"); // 连接,12位
v3 = strlen(Destination);
sub_40100A((BYTE *)Destination, v3, String1); // 加密
if ( !_strcmpi(String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )// 比较
{
printf("continue...\n\n");
printf("pls input the first passwd(2): ");
memset(Str, 0, sizeof(Str));
scanf("%s", Str); //
if ( strlen(Str) != 6 )
{
printf("Must be 6 characters!\n"); // 6位
ExitProcess(0);
}
strcat(Str, Destination); // 连接
memset(String1, 0, sizeof(String1)); // 初始化
v4 = strlen(Str);
sub_401019((BYTE *)Str, v4, String1); // 加密
if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", String1) )// 比较
{
if ( !(unsigned __int8)sub_40100F(Str) )
{
printf("Error!!\n");
ExitProcess(0);
}
printf("bye ~~\n");
}
}
return 0;
}
关于两种加密算法的识别:
(1)SHA加密 ,里面调用了一堆win32的API函数,查看官方文档发现关键函数是CryptCreateHash,其中的0x8004为SHA算法的特征值
int __cdecl sub_401230(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
int result; // eax
DWORD i; // [esp+4Ch] [ebp-28h]
CHAR String2[4]; // [esp+50h] [ebp-24h] BYREF
BYTE v6[20]; // [esp+54h] [ebp-20h] BYREF
DWORD pdwDataLen; // [esp+68h] [ebp-Ch] BYREF
HCRYPTHASH phHash; // [esp+6Ch] [ebp-8h] BYREF
HCRYPTPROV phProv; // [esp+70h] [ebp-4h] BYREF
if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
return 0;
if ( CryptCreateHash(phProv, 0x8004u, 0, 0, &phHash) )// 创建加密服务,提供程序(CSP)哈希对象的句柄
{
if ( CryptHashData(phHash, pbData, dwDataLen, 0) )
{
CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);// 检索控制散列对象操作的数据
*lpString1 = 0;
for ( i = 0; i < pdwDataLen; ++i )
{
wsprintfA(String2, "%02X", v6[i]); // 将字符和数值输入到缓冲区
lstrcatA(lpString1, String2); // 该函数将一个字符串附加在另一个字符串后面。
}
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
result = 1;
}
else
{
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
result = 0;
}
}
else
{
CryptReleaseContext(phProv, 0);
result = 0;
}
return result;
}
(2)MD5加密,与上一个差不多,不过CryptCreateHash函数中的值变为0x8003,是MD5加密的特征值
int __cdecl sub_401040(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
int result; // eax
DWORD i; // [esp+4Ch] [ebp-24h]
CHAR String2[4]; // [esp+50h] [ebp-20h] BYREF
BYTE v6[16]; // [esp+54h] [ebp-1Ch] BYREF
DWORD pdwDataLen; // [esp+64h] [ebp-Ch] BYREF
HCRYPTHASH phHash; // [esp+68h] [ebp-8h] BYREF
HCRYPTPROV phProv; // [esp+6Ch] [ebp-4h] BYREF
if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
return 0;
if ( CryptCreateHash(phProv, 0x8003u, 0, 0, &phHash) )
{
if ( CryptHashData(phHash, pbData, dwDataLen, 0) )
{
CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);
*lpString1 = 0;
for ( i = 0; i < pdwDataLen; ++i )
{
wsprintfA(String2, "%02X", v6[i]); // 将数据写入缓存区
lstrcatA(lpString1, String2); // 连接
}
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
result = 1;
}
else
{
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
result = 0;
}
}
else
{
CryptReleaseContext(phProv, 0);
result = 0;
}
return result;
}
[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;//获取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);});
//replace(a,b)将a指定的内容用b替换,此处的a为正则表达式,[a-zA-Z]表示匹配a-z与A-Z中的所有字符,g表示进行全局匹配,即比较flag中的每个字符,未加g时匹配到一个指定字符便会停止;
//fromCharCode()将 Unicode 编码的值转换为一个字符串;
//b? x : y为三目运算符,b为真返回x否则返回y;
//charCodeAt(a)返回字符串第a个字符的 Unicode 编码。
//经分析,此为Rot13加密算法
if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) {
alert("Correct flag!");
} else {
alert("Incorrect flag, rot again");
}
}
</script>
</body>
</html>
python脚本
rotflag = 'PyvragFvqrYbtvafNerRnfl@syner-ba.pbz'
flag = ''
for i in rotflag:
if i >= 'a' and i <= 'z':
flag += chr((ord(i) - ord('a') + 13) % 26 + ord('a'))
elif i >= 'A' and i <= 'Z':
flag += chr((ord(i) - ord('A') + 13) % 26 + ord('A'))
else:
flag += i
print(flag)
[2019红帽杯]easyRE
查壳,64位无壳。
IDA打开,先看字符串
看到
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
应该是base64加密,跟进到sub_400E44(),确实是base64加密。
再跟进"You found me!!!",找到sub_4009C6,F5。
前半部分,用脚本解密
伪代码中的字符串出错了,还得看汇编代码,本来还没问题的,就很后怕,还是要以汇编代码为主才行。
v15 = ""
v13 = "y.i"
v14 = "d`3w}wek9{iy=~yL@EC"
print(len(v14))
v12 = "Iodl>Qnb(ocy"
for i in range(len(v12)):
v15 += chr(ord(v12[i]) ^ i)
v15 += chr(127 ^ 12)
for i in range(len(v13)):
v15 += chr(ord(v13[i]) ^ (i + 13))
v15 += chr(127 ^ 16)
for i in range(len(v14)):
v15 += chr(ord(v14[i]) ^ (i + 17))
print(v15)
得到
再解密后半部分,函数sub_400360功能类似于比较运算符。
最终得到一网址https://bbs.pediy.com/thread-254172.htm,点进去是看雪的一个帖子,然后发现被坑了......
回去再看看字符串,其中/$nb<'THl$nr<2E[很特别,地址在Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xV下方跟进sub_4009C6
unsigned __int64 sub_400D35()
{
unsigned __int64 result; // rax
unsigned int v1; // [rsp+Ch] [rbp-24h]
int i; // [rsp+10h] [rbp-20h]
int j; // [rsp+14h] [rbp-1Ch]
unsigned int v4; // [rsp+24h] [rbp-Ch]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
v1 = sub_43FD20(0LL) - qword_6CEE38;
for ( i = 0; i <= 1233; ++i )
{
sub_40F790(v1);
sub_40FE60();
sub_40FE60();
v1 = sub_40FE60() ^ 0x98765432;
}
v4 = v1;
if ( ((unsigned __int8)v1 ^ byte_6CC0A0[0]) == 'f' && (HIBYTE(v4) ^ (unsigned __int8)byte_6CC0A3) == 'g' )
{
for ( j = 0; j <= 24; ++j )
sub_410E90((unsigned __int8)(byte_6CC0A0[j] ^ *((_BYTE *)&v4 + j % 4)));
}
result = __readfsqword(0x28u) ^ v5;
if ( result )
sub_444020();
return result;
}
f与g是第一个与第三个也正好是flag的第一个和第三个字符,因此flag就在这了,这是有理有据的猜。
python脚本
lis = [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]
str = 'flag'
v4 = []
for i in range(4):
v4.append(ord(str[i]) ^ lis[i])
flag = ''
for i in range(25):
flag += chr(lis[i] ^ v4[i % 4])
print(flag)
得到flag{Act1ve_Defen5e_Test}
虽然自己没做出来,但仍然收获很大。
- 汇编代码很重要,反汇编代码与字符串的异常需要通过汇编代码矫正,有些线索也在汇编代码中
- 某些位置的自定义函数与标准函数功能相同,如memset下的sub_4406E0与scanf功能相同,if判断条件中的sub_424BA0与strlen功能相同以及sub_400360与strcmp功能相同,大部分时候都是这样的