二进制逆向题目解析
1.给你个礼物你能收到吗
打开程序你会发现,它让你输入提取码:
你可以随便输入几个字符,会出现:
如果一直输入错无的话,它就会陷入一个循环:
所以,先用ida打开,用Shift+12调出字符串表,你会发现字符串表没有汉语,而程序出来的提示是汉语,所以我们无法在字符串表中找到我们想要的字符串,那么我们只能从在左侧的函数列表里查看main函数,直接进入主函数:
点开main函数,可以进入一个主函数,
int __cdecl main(int argc, const char **argv, const char **envp)
{
_main();
present();
heart();
system("pause");
return 0;
}
然后你会发现有一个present(),这应该是我们获得礼物的函数入口,你点进去,会看到另外一串代码:
void __cdecl present()
{
__int64 v0; // rax
std::ostream *v1; // rax
unsigned int x; // [rsp+24h] [rbp-5Ch]
int xa; // [rsp+24h] [rbp-5Ch]
int xb; // [rsp+24h] [rbp-5Ch]
int xc; // [rsp+24h] [rbp-5Ch]
int xd; // [rsp+24h] [rbp-5Ch]
int xe; // [rsp+24h] [rbp-5Ch]
int xf; // [rsp+24h] [rbp-5Ch]
int n; // [rsp+28h] [rbp-58h]
int y; // [rsp+2Ch] [rbp-54h]
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, asc_47F000);
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, &byte_47F050);
x = 0;
y = 2027091370;
std::istream::operator>>(&std::cin, &n);
while ( n != y )
{
if ( n == 5201314 )
{
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, &byte_47F068);
system("pause");
exit(0);
}
if ( !(y % 40) )
y = 2027091370;
v0 = std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, &byte_47F0A0);
v1 = (std::ostream *)std::ostream::operator<<(v0, x);
std::operator<<<std::char_traits<char>>(v1, &byte_47F0C3);
std::istream::operator>>(&std::cin, &n);
++x;
y += 7;
}
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, &byte_47F0D8);
for ( xa = 0; xa <= 78; ++xa )
{
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, "[");
if ( xa <= 19 )
Sleep(0xC8u);
if ( xa > 74 )
Sleep(0x3E8u);
}
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, asc_47F0F8);
std::istream::operator>>(&std::cin, &n);
if ( n != 1 )
exit(0);
std::operator<<<std::char_traits<char>>(
(std::ostream *)&std::cout,
" |\\ /| \n");
std::operator<<<std::char_traits<char>>(
(std::ostream *)&std::cout,
" | \\________/ | \n");
std::operator<<<std::char_traits<char>>(
(std::ostream *)&std::cout,
" \\/|______|\\/ \n");
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, "|");
for ( xb = 0; xb <= 38; ++xb )
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, "—");
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, "|\n|");
for ( xc = 0; xc <= 77; ++xc )
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, "-");
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, "|\n|");
for ( xd = 0; xd <= 38; ++xd )
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, "—");
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, "|\n");
for ( n = 0; n <= 4; ++n )
{
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, " |");
for ( xe = 0; xe <= 71; ++xe )
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, " ");
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, "| \n");
}
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, " ");
for ( xf = 0; xf <= 73; ++xf )
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, "@");
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, asc_47F248);
std::istream::operator>>((std::istream *)&std::cin);
if ( xf != 1 )
exit(0);
}
根据刚开始进入程序是的分析,代码里应该是有一个循环,如果我们输入错误,会执行这个循环,那么,我们要做到就是跳出这个循环,我们观察代码,找到这个循环:
while ( n != y )
{
if ( n == 5201314 )
{
std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, &byte_47F068);
system("pause");
exit(0);
}
if ( !(y % 40) )
y = 2027091370;
v0 = std::operator<<<std::char_traits<char>>((std::ostream *)&std::cout, &byte_47F0A0);
v1 = (std::ostream *)std::ostream::operator<<(v0, x);
std::operator<<<std::char_traits<char>>(v1, &byte_47F0C3);
std::istream::operator>>(&std::cin, &n);
++x;
y += 7;
}
我们在循环里寻找跳出循环的语句,找到了 exit(0);
,找到之后,看看怎么获得,如果n=5201314,可以执行exit(0);
, 所以,我们输入5201314,看一看,发现,没有得到礼物:
还是错误,当年任意键继续时,会发现它退出了程序,所以,我们可知这里的exit(0);
是退出程序的,并不能退出循环,所以,我们不能寻找到跳出循环的语句,那么为了不执行这个循环,我们只能使循环条件不成立,所以,使n=y,即n= 2027091370,我们输入 2027091370;
会发现,我们得到了礼物,但是,如果我们已经输入错一次,在输入2027091370,那就还是错的:
那么这是为什么呢?我们看,while循环的最后一句:y+=7
这句是说每执行执行一次循环,y的值就会加7,所以,如果你已经输入错了一次,要想输入正确提取码,是while循环不成立,就要在 2027091370后面加7,正确的前面错了几次,就要加上几个七:
链接:C语言中Exit函数的使用
2.pyc文件
网上百度一个python反编译在线工具,反编译上述pyc文件,得到一串代码:
#!/usr/bin/env python
# visit http://tool.lu/pyc/ for more information
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'
我们从下往上推:
correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
flag = ''
print 'Input flag:'
flag = raw_input()
if encode(flag) == correct:
print 'correct'
else:
print 'wrong'
我们可知,flag是字符,输入flag,如果经过 encode()函数得到的结果是correct,则输出的是correct,否则,错误,我们已经知道correct= ‘XlNkVmtUI1MgXWBZXCFeKY+AaXNt’和encode()函数,反推得到flag即可,我们来看一下encode()函数:
def encode(message):
s = ''
for i in message:
x = ord(i) ^ 32
x = x + 16
s += chr(x)
return base64.b64encode(s)
从后面得知,encode(flag)=correct,那么根据上面的函数可知,message就是flag,则最后输出的correct就是函数里最后得return返回的值,我们反推回去,base64.b64encode(s),这句是指把s进行base64进行加密,所以我们应该先把correct解密回去,所以我们可以写Python脚本:
import base64
correct='XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
s=base64.b64decode(correct)
然后求flag,根据encode函数可知,知道s后应该求x,x是s中每一个字符的ASCII值,然后用ord()函数求x,x减去十六后在复制给x,再往上一步是x是flag中的每一个值得ASCII值与32异或,所以flag中的字符就是x与32异或所得的数在ASCII表中对应的字符,所以,我们可以写python脚本:
flag=''
for i in range(0,len(s)):
x = ord(s[i])
x = x-16
flag +=chr(x^32)
所以,我们就可以写出脚本,运行得到flag:
import base64
correct='XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
s=base64.b64decode(correct)
flag=''
for i in range(0,len(s)):
x = ord(s[i])
x = x-16
flag +=chr(x^32)
print flag
输出的结果:
nctf{d3c0mpil1n9_PyC}
链接:python中base64模块的加解密函数
链接:Python chr() 函数
链接:Python ord() 函数