一.总结
- 句柄的概念:逻辑上是二级指针,数值上是32位无符号整数
- CSP(Cryptographic Service Providers):加密服务提供程序
- ALGID:用于指示使用的哈希算法,0x8003指示
MD5,0x8004指示SHA1 - md5在线解密网站(其他网站亲测没用)
二.基本步骤
32位程序,打开后找到main->main0,即可查看关键代码
程序大概步骤是:
- 输入第一个6字符长的字符串destion,假设是123321,将字符串和"@DBApp"拼接构成"123321@DBApp"
- 将destion字符串进行sha1哈希加密,string1保存对应的哈希值,比较哈希值
- 输入第二个6字符字符串,假设是 ~!3a@0,再将上文的"123321@DBApp"拼接到第二次输入的字符串的后方构成 str字符串"~!3a@0123321@DBApp"
- 将str字符串进行md5哈希加密,string1保存对应md5值,比较md5值
- 最后一个判断,不怎么影响结果,所以只要把str字符串对应md5值解密即可
三.关键函数
1.主函数
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
DWORD destion_len; // 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); // 必定是6位
if ( strlen(Destination) != 6 )
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
v7 = atoi(Destination);
if ( v7 < 100000 ) // 预计v7并不怎么会影响运行
ExitProcess(0);
strcat(Destination, "@DBApp"); // 加上了@DBAPP
destion_len = strlen(Destination);
sub_40100A((BYTE *)Destination, destion_len, String1);// sha1哈希处理,string1保存了destination的40位哈希值
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 ) // 也是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); // 0x8003u,md5哈希算法
if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", String1) )
{
if ( !(unsigned __int8)sub_40100F(Str) ) // 并不影响结果
{
printf("Error!!\n");
ExitProcess(0);
}
printf("bye ~~\n");
}
}
return 0;
}
2.sub_40100A(sha1哈希加密函数)
点进去找到sub_401230函数
这里的一些关键函数都可以百度查windows api查到
- CryptAcquireContextA应该是获取CSP句柄权限
- CryptCreateHash创建数据流哈希对象,第二个参数0x8004u代表sha1加密
- CryptHashData把数据添加到指定哈希对象,这个应该就是进行哈希处理
- CryptGetHashParam检索实际哈希值,这个应该是用于把哈希对象的哈希值赋给v6数组
- wsprintfA将数据写入指定缓冲区,这里用于字符串复制
- lstrcatA类似于strcat函数功能,也适用于复制
- ALG_ID加密使用的哈希算法的id,0x8003表示sha1,0x8004表示md5
// pbdata是传入的字符数组指针,dwdatalen是数组长度,lpstr是字符指针
int __cdecl sub_401230(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
DWORD i; // [esp+4Ch] [ebp-28h]
char String2[4]; // [esp+50h] [ebp-24h] BYREF
char 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 csp句柄
if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )// 获取csp句柄权限?
return 0;
if ( CryptCreateHash(phProv, 0x8004u, 0, 0, &phHash) )// 启动数据流哈希对象,这里参数是0x8004u,控制选择何种哈希算法,查询得知这是sha1
{
if ( CryptHashData(phHash, pbData, dwDataLen, 0) )// 将数据添加到指定哈希对象,用于计算哈希值
{
CryptGetHashParam(phHash, 2u, (BYTE *)v6, &pdwDataLen, 0);// 检索实际哈希值
*lpString1 = 0; // 内容清空
for ( i = 0; i < pdwDataLen; ++i )
{
wsprintfA(String2, "%02X", (unsigned __int8)v6[i]);// 将数据写入指定缓冲区
lstrcatA(lpString1, String2); // 将一个字符串追加到另一个字符串,这里是把哈希值复制给string1
}
CryptDestroyHash(phHash); // 销毁引用的哈希对象
CryptReleaseContext(phProv, 0); // 释放csp(加密服务提供程序)句柄
return 1;
}
else
{
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
return 0; // 失败返回0,其他关闭操作相同
}
}
else
{
CryptReleaseContext(phProv, 0);
return 0;
}
}
3.sub_401019(md5哈希加密)
点进去看到sub_401040,在点进去和上一个函数对比可以发现这个是md5哈希加密
4.atoi(这个函数不清楚具体作用)
猜测是返回输入字符串例如"123321"对应整数数值123321
int __cdecl atol(const char *String)
{
int v4; // [esp+8h] [ebp-Ch]
int v5; // [esp+Ch] [ebp-8h]
int C; // [esp+10h] [ebp-4h]
const char *Stringa; // [esp+1Ch] [ebp+8h]
while ( (int)SrcSizeInBytes <= 1
? *((_WORD *)off_428A60 + *(unsigned __int8 *)String) & 8
: _isctype(*(unsigned __int8 *)String, 8) )
++String;
C = *(unsigned __int8 *)String; // String首字符
Stringa = String + 1;
v4 = C; // 也是String首字符
if ( C == '-' || C == '+' )
C = *(unsigned __int8 *)Stringa++;
v5 = 0;
while ( (int)SrcSizeInBytes <= 1 ? *((_WORD *)off_428A60 + C) & 4 : _isctype(C, 4) )
{
v5 = 10 * v5 + C - '0'; // 盲猜v5等于输入的字符串的对应数值,例如123456
C = *(unsigned __int8 *)Stringa++;
}
if ( v4 == '-' )
return -v5;
else
return v5;
}
通过MD5在线解密"27019e688a4e62a649fd99cadaafdb4e",得到"~!3a@0123321@DBApp
"此时可以知道第一个输入的字符串就是123321,第二个是~!3a@0
运行程序输入对应字符串后,程序会在其所在目录创建一个dbapp.rtf文件
打开可以得到flag{N0_M0re_Free_Bugs}