本题是dark ctf Reverse的第一题:so_much,网址:https://ctf.darkarmy.xyz/challs 题目描述:strcmp printf
第一次打比赛,不过是自己一个,只写了Rev的题。8道Rev的题只做出来4道…还得继续努力
扔进IDA里分析看看,发现报了好多警告:
先不管警告,进去看看再说。Let's have an argument. Shall we? 在main函数中被使用。 main函数中,先将argv保存到v10,v10[1]就是用户的输入。接下来调用get_flag(v3, &v13, &v11, argv);函数,但是没有返回值。不过传进去的是变量的地址,所以应该还是可以修改变量的值的。 往下看,外层的if判断了参数的个数必须为2个,即./so_much 和 用户输入 这两个参数。然后将v11赋值给argv,但是后面没有用到argv了,所以这个操作好像没啥用。 然后在里层的if判断中,调用了sub_10C0函数进行判断:if ( sub_10C0(v10[1], v11) ),只有当函数返回值为0时,才会打印出正确的flag。
以下为试错的过程,如果不想看的可以跳到2.2节
那么现在得先看一下get_flag函数的内容了。在get_flag函数里,a3是main函数中的v11,是我们需要关注的变量。但是a3只被v7赋值了。。。 什么鬼。。 而且for循环中的flag_48跟进去以后,一层套一层,从flag_48到了flag_125,把我看吐了= =||
而且有的函数本来是有参数的,我双击跟进去发现是类似下面的代码:
__int64 sub_10D0() { __asm { endbr64 } return sub_1070(); }再回到get_flag函数时,整个函数的参数就已经没了。总感觉IDA是不是F5出毛病了。。
索性扔到ghidra里看看呢。。一下子就看懂了,s2是内存地址0x2008开始的一个字符串,然后经过get_flag函数修改,在于用户的输入argv[1]比较,当strcmp返回值为0、即二者相等时,输出flag。
... s2 = (char **)0x2008; get_flag((int64_t)&s2); if (argc == 2) { iVar1 = strcmp(argv[1], s2, s2); if (iVar1 == 0) { printf("darkCTF%s\n", s2); .plt.sec("WoW! so much revving..."); ... ... ...那就好办了,直接gdb动态调试,最后得到s2经过get_flag后的值为:{w0w_s0_m4ny_funct10ns}。
运行程序,得到flag:darkCTF{w0w_s0_m4ny_funct10ns}
$ ./so_much {w0w_s0_m4ny_funct10ns} darkCTF{w0w_s0_m4ny_funct10ns} WoW! so much revving...有时候IDA识别不好的,可以试试ghidra,再不济,上gdb或ollydbg动态调试。
同时,题目中有时候会使用疑兵之计,比如本题中get_flag函数里for循环中的flag_xx函数系列。 如果当真搁那儿算半天,或是用python重写那几十个函数的调用,那可真是浪费时间了。 遇到这种工作量比较多的,不妨先往下看,把整个程序的脉络搞清楚,如果工作量大的那部分确确实实跳不过去,需要去逆,那再回头逆它也不迟。