代码片段和注释很长,因此我将它们包含在单独的HTML链接中。
反编译的C -Code
ELF可执行文件
//Gives you an overview of what this program does in C. int main(int argc, char ** argv) { int32_t result; // success value if (argc > 1) { setup(); //Allocates an integer array (g1) int32_t v1 = 0; //counter while (true) { char v2 = *(char *) (*(int32_t *) ((int32_t)argv + 4) + v1); int32_t v3 = *(int32_t *) (4 * v1 + (int32_t) & g1); int32_t v4 = v1 + 1; //index in g1 array if (check( (int32_t *) v3, (int32_t) v2) != 0) { puts("Nope"); return -1; } if (v4 >= 31) //when we've reached our last index in array { break; } v1 = v4; } puts("congrats!"); result = 0; } else { puts("usage ./smoothie "); result = -1; } return result; }
我尝试使用GDB调试程序和IDA以了解控件-流。我的推论最后涉及三件事:
setup()生成某种整数数组
check(int32_t * array,int * array)被调用31次以确认我们如可能的while循环所示,“ password”在check(...)内,它通过每个malloc数组[5]并确认密码的每个“字符”。
这是IDA的check()控制流程图(简单-3个部分):
这是check()的控制流程图点击放大:
大多数check()比较*(数组)为上半部分和*(数组+ 8)表示下半部分,再加上passwordIndex比较。
在该程序的main(int argc,char ** argv)中,有一个while循环需要执行31次(密码长度可能为31个字符)。如果check()返回1,则while循环将退出。
我认为中间部分会进行一些有用的计算(add,sub,cdq和idiv,imul和xor)。
请引导我正确的方向(如果可以,请提供提示)。自己调试程序可能会让我的问题更清楚。 “我的输入ascii应该匹配的正确数字(例如,计算得出的90,因此index [i]应该等于'Z'〜90)。
在检查的后半部分,它使用*(数组+ 8),这意味着它在101、142,...,数字比较中使用数组的第三个数字。我们可以将*(数组+ 8)的解引用值更改为计算得出的数字(90),以进行最终比较。的支票数,因此90 ==90。由于它们相等,因此函数成功返回0。由于数组中的预设值而相同。我该怎么做才能纠正这个问题?
#1 楼
解决此CTF的方法是同时分析setup()
和check()
方法(duh!)。第一个很长,但是当您检查正在发生的事情时,这很简单。它正在分配20B的内存来存储5 x 4 B的数据。它执行了31次。在那里存储的值看起来像是随机的,但不是。第一个和第三个位置只有5个不同的值。第一个和第三个插槽中的值是操作类型值;第三,第四-操作数; 5-操作数和结果
如果我们检查
check()
函数。我们看到正在传递的参数是:在标志的setup()
和char
中创建并填充的缓冲区之一。根据缓冲区中找到的值,可以对缓冲区中的值进行数学运算。有关该运算的更多信息。基于第一和第三值的数量很少。如果我们以第一个缓冲区的值为例:0x01 0x81 0x65 0x0C 0x5A 0x01-修改操作,0x65-添加;因此字符为(0x5A%0x81)+ 0x0C。我们在每个缓冲区上进行模拟(但操作不同)。
此二进制文件有什么问题(或者可能是故意的)?好吧,几乎在所有情况下,操作数上计算出的值都不会与标志的char进行比较,并且此
eax
的设置不正确,我们得到“无”。需要解决的是更改跳转,以便所有情况都通过检查标志是否为char。除了修改二进制文件之外,您可以做的就是欺骗gdb以使其正常运行。在
leave
上设置断点type命令
/> type p / c $ edx
type set $ eax = 0
type end
这样,只要触发断点,就将打印计算出的字符,并且它将模拟比较正常,并允许应用程序继续。有了它,您可以运行该应用程序,点击几次“ c”并观察标志
最后是标志
标志{HereBeDaFlagForYoDebu?g? h}
还有一些解释...
这两个'?'控制字节错误或操作数错误,因此它们的计算错误。也许我会花一些时间找出并尝试正确地计算它们,但是使用当前值,我们得到的结果超出了ASCII可打印的范围。
#2 楼
编辑:正如PawełŁukasik和DittoPDX所说,二进制文件确实存在缺陷,因此不允许使用任何正确的标志。但是,下面的方法仍然是有效的,并且可以在类似的任务中提供帮助。只需很少的逆向工程即可完成的任务。整个解决方案取决于构建主循环的方式:
for ( i= 0; i <= 30; i++)
{
if ( check(ec[i], argv[1][i]) )
{
puts("Nope");
return -1;
}
}
puts("congrats!");
result = 0;
唯一的方法达到“恭喜!”
有趣的是,对输入的每个字符调用check函数,并且循环在无效时就存在。
br />
这种构造对您可以在程序执行期间评估的事物有一些暗示。可以将其称为“旁道攻击”。
扰流板,在此标记下方是解决此任务的更多详细信息。
我的意思是:每获得一个正确的char,二进制执行的指令数量就会增加。
解决这一难题的一种方法是计算已执行指令的数量(提示:PinTools;源代码/工具/ ManualExamples /实例),并通过char暴力破解输入的char。每当您猜到正确的char时,执行指令的数量就会增加。
编写所有脚本的简单方法是使用pwntools python模块:)
希望这对CTF有所帮助!
评论
提到上面Lukasik的“有缺陷的二进制”注释方式,我认为即使我确实找到了密码,程序也不会接受它,因为它需要我们手动调试程序并找出每个单独的ascii字符。如果我们在扩展的ascii表中输入(0-256)的任何单个输入,则无论该数组如何,都将返回false。为了通过第二组检查,我们必须手动更改数组值以匹配其计算值。
–user21677
17/09/22在2:00
评论
所以你在这里问什么?我添加了check()的整个控制流图。通过检查0x65至0xfa,它们在某种程度上与分配的数组中的值匹配。检查的前半部分对应于*(数组),后半部分对应于*(数组+ 8)。我可以在GNU调试器中做一些事情来通过数组传递这些检查吗?
那是什么周大福?
CTF-Sparsa。调试-此处未上传200(或冰沙):github.com/pchaigno/ctf-sparsa。这就是比赛的基本样子(危险的风格)。
好的,我已经分析了二进制文件并知道标记具有99%的概率,但是当前的二进制文件存在缺陷。对于今天,我唯一能提供的建议就是尝试分析设置并检查(尤其是它与设置之间的关系),您将了解如何获取标志。但是,再次-二进制是错误的-我明天晚些时候发布(现在是午夜)