深入浅出,知无不答,言无不尽,力求详尽
下载附件, 老样子查壳
无壳放心反编译, IDA!启动!
直接F5,
main函数都不用跳转直接就是
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // ebx
char v4; // al
int result; // eax
int v6; // [esp+0h] [ebp-70h]
int v7; // [esp+0h] [ebp-70h]
char Buffer[2]; // [esp+12h] [ebp-5Eh] BYREF
char v9[20]; // [esp+14h] [ebp-5Ch] BYREF
char v10[32]; // [esp+28h] [ebp-48h] BYREF
__int16 v11; // [esp+48h] [ebp-28h]
char v12; // [esp+4Ah] [ebp-26h]
char v13[36]; // [esp+4Ch] [ebp-24h] BYREF
strcpy(v13, "437261636b4d654a757374466f7246756e");
while ( 1 )
{
memset(v10, 0, sizeof(v10));
v11 = 0;
v12 = 0;
sub_40134B(aPleaseInputYou, v6);
scanf("%s", v9);
if ( strlen(v9) > 0x11 )
break;
for ( i = 0; i < 17; ++i )
{
v4 = v9[i];
if ( !v4 )
break;
sprintf(Buffer, "%x", v4);
strcat(v10, Buffer);
}
if ( !strcmp(v10, v13) )
sub_40134B(aSuccess, v7);
else
sub_40134B(aWrong, v7);
}
sub_40134B(aWrong, v7);
result = --Stream._cnt;
if ( Stream._cnt < 0 )
return _filbuf(&Stream);
++Stream._ptr;
return result;
}
虽然简单, 依然的逐行分析。
1、第一行主函数参数, argc和argv不再赘述, 详细看上一篇文章
const char **envp
这句代码的含义不像看起来那样, 不是简单的创建一个常量 字符型 二级指针, envp在main函数里是有特殊含义的, 它一般代表系统中的环境变量, 而且内容非常详细, 有的小伙伴可能会问了, "有多详细?", 这你得去问baidu, 我怎么知道多详细...我的脑袋又不是通电才能工作。
2、3-13行初始化变量数据类型。(c语言基础)
3、strcpy函数接受了一串数据"437261636b4d654a757374466f7246756e"传递给了v13, 等价于
v13 = "437261636b4d654a757374466f7246756e"
4、接着进入循环体。
利用memset函数把v10这个字符型数组数据初始化成0, v11和v12赋值为0。
接下来这个sub_40134B点进去分析一通(叽里咕噜在干啥, 初学者看不懂直接当成内存操作就行了),
对内存进行了很多移位操作, 还有部分代码进行了文件校验, 具体细节还不是我这个小菜鸡需要分析的(毕竟这题难度才4)。
scanf函数接受我们的输入赋值给v9。
如果我们输入的数据超过0x11(十进制17), 直接跳出循环。
然后进入for循环, 循环17次, 遍历v9赋值给v4, v4如果是0则打断循环, 任何非0的字符和数字都是1(真值), 我们要让下面的语句执行, 输入的值里就不能包含0。
sprintf(Buffer, "%x", v4);
strcat(v10, Buffer);
sprintf函数举个例子说明, sprintf(a, b, c), a变量会被以b的格式赋值为c, c会被先以b的方式处理再赋值给a。所以这里的Buffer会等于v4(%x无符号十六进制转换), Buffer在编程里是缓冲的意思, 这是一个缓冲变量, 就是暂存数据的变量。
strcat函数类似python里的列表append方法, 前一个参数是需要添加元素的数组, 后一个参数是被添加进数组末尾的数据。
if ( !strcmp(v10, v13) )
sub_40134B(aSuccess, v7);
else
sub_40134B(aWrong, v7);
接下来把v10数组和v13数组比较, 两者相等才能输出aSuccess, 否则就aWrong咯。
sub40134B还是不看。
5、最后一段
sub_40134B(aWrong, v7);
result = --Stream._cnt;
if ( Stream._cnt < 0 )
return _filbuf(&Stream);
++Stream._ptr;
return result;
这一段其实是对输入流的处理, 需要一部分高级编程知识, stream._cnt访问了 stream这个对象中的_cnt成员, 这个成员一般表示当前缓冲区中剩余可读取的字符数, 后面的判断检测它还有没有数据未读取, _filbuf函数填入新的流数据。
如果还有数据, 这里将stream对象中的)_ptr成员(通常指向当前缓冲区中下一个要读取的字符的位置)加1, 更新缓冲数据的位置, 最后返回流, 确保数据正确的更新。
其实逻辑已经很清晰了, 既然我们输入的数据要等于"437261636b4d654a757374466f7246756e"
那就逆回来我们输入的数据呗, 这里面唯一对我们输入数据进行处理的地方, 就是第30行, sprintf函数把我们输入的数据变成了无符号十六进制, 而且填入了只有两个位的数组, 也就是说要从v13中两个两个取出再转换成ASCII码, 就能得到flag了。
python代码如下:
flag:
CrackMeJustForFun