查壳
先运行看看是个啥:
要我们输入密码(1),估计还有密码(2),随便输了就直接给你退出,先不管密码(2)是否存在遇到了再说。
32位,进IDA找主函数:
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");
ExitProcess(0);
}
v7 = atoi(Destination);
if ( v7 < 100000 )
ExitProcess(0);
strcat(Destination, "@DBApp");
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");
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;
}
得到一个挺长的函数,老思想,先找正确的输出的判断:
是将“27019e688a4e62a649fd99cadaafdb4e”与String1对比,才有可能输出成功,那么我们来找Stirng1,先看它内存里有东西吗
空的,那么去找相关的赋值或者方法:这里要从头开始看,不然容易漏掉加密过程。
首先是memset方法,初始化空间嘛,不用理,往下看我们能看到sub_40100A方法调用了String1和Des,看起来有点像将Des的值给String1,不要感觉,我们去追求的是实际,那么我们去找Des:
可以知道Des是由我们来输入的,而且一定是6位数或者6个字符,接着往下看:
v7将我们输入的6个字符转化成了数字,并要求不能小于100000,否则退出,那么可以知道我们输入的Des范围一定是100000 ~ 999999(看到范围,第一个想干嘛,肯定是爆破昂(我知道你很急,但你先别急,我们接着看判断)):
是说将Des字符串上加上“@DBApp”后经过sub_40100A方法(出现了第一次涉及String1的方法)加密成一段密文后与"6E32D0943418C2C33385BC35A1470250DD8923A9"比较,正确则出现输入第二段密码的字样,跟进方法看看:
一堆看不懂的函数,查呗:(发现一个加密算法Hash标志)
可以知道这是Hash加密算法,这里不介绍了,0x8004表示具体加密形式:SHA1,可以自己去查:上脚本:
import hashlib
ad = '@DBApp'
Des = '6E32D0943418C2C33385BC35A1470250DD8923A9'.lower()
for i in range(100000, 1000000):
ans = str(i) + ad
end = hashlib.sha1(ans.encode('utf-8')).hexdigest()
if end == Des:
print(i)
break
这里注意:为什么要进行一个小写的转化呢,因为hash加密后的密文一般都是小写的。
可以得到第一个密码123321,确认一下:
继续:
又读入一个长度为6的字符串,作为第二个密码:看判断:
将Des接入输入的密码后边,进入sub_401019方法加密后与"27019e688a4e62a649fd99cadaafdb4e"比较:看方法:
sub_401019:发现与上边差不多相同:(又是Hash)但是仔细观看,会发现参数的不同 0x8003u --> md5加密
我们又知道了加密后的文本,直接解密呗:
得到一个字符串:"~!3a@0123321@DBApp"仔细一看?完整密码?直接过了 密码(2):~!3a@0
输入后得到一个文件dbapp.rtf打开后直接得到Flag{N0_M0re_Free_Bugs}可以收工了。
在做第二次判断的时候,我们发现内层里边里还有一个判断,是只针对Str来的,而Str就是密码(2),那么我们可以去看看这是个啥:
跟进sub_40100F方法:
发现似乎从"AAA"文件中拿东西,(https://blog.csdn.net/bygwys/article/details/124206536)参考文献。然后进入sub_401005解密,可能有人会问,从哪开始拿,拿几位? --> 一般文件都是从头开始拿,那么我们就从头开始拿就好了,那么拿几位呢?因为Str是六位嘛,那么我们拿六位出来解密:
跟进sub_401005方法:
发现是一个xor,那么问题又来来了,跟谁异或呢(在方法里面发现没有明确的东西异或),由于要生成.rtf文件,那么AAA中的数据跟什么异或一定得到"{\rtf1"(.rtf文件头的前六位(为什么是6位呢,因为密码就6位昂))这个呢:(异或的特性不就来了嘛)
Des = [0x05, 0x7D, 0x41, 0x15, 0x26, 0x01]
Des2 = '{\\rtf1'
pas = ''
for i in range(len(Des)):
pas += chr(Des[i % len(Des)] ^ ord(Des2[i]))
print(pas)