巅峰极客比赛中逆向第二题:Input you lucky number

记一次CTF比赛中,使用python对exe程序中交互爆破flag-Alkaid

这道题首先我们使用IDA分析:

main函数中通过cin接收输入,下面做了一大堆操作 但是通过对input变量的跟踪,可以发现都没有什么卵用 与输入无关,因此我们根本不需要去读,反正都是正向操作

记一次CTF比赛中,使用python对exe程序中交互爆破flag-Alkaid

只有箭头所指的部分才需要关注

于是查看sub_401100

最开头这三个处理非常丑,要不挨个去查手册,要不就直接动调观察 汇编指令是三条

全部是交叉赋值的,分别将1字节的input变为2字节、4字节、16字节

记一次CTF比赛中,使用python对exe程序中交互爆破flag-Alkaid

(试了OD和IDA都看不到XMM寄存器的值,最后用x32dbg才成功跟踪到) 下面两段循环实际上都是逐字节异或 因为第一段以XMM寄存器来操作,单位必须是16个字节,对于零头就无能为力了,于是使用第二段循环来逐字节操作 不知道是出题人故意设置的干扰还是编译器为了加速而优化出来的23333

记一次CTF比赛中,使用python对exe程序中交互爆破flag-Alkaid

那么下一个问题就是key啦 回头看参数a2对应的值,是0x401000+v6处的代码 观察加密后的HEX

记一次CTF比赛中,使用python对exe程序中交互爆破flag-Alkaid

(动调可知起始字节在0x89之后)

这里有大量的连续0x5A出现,在PE文件中通常会有大量的0x00作为填充,这里由于加密字节数太少所以没有用到上述的论据 不过连续三个0x5A通常一般是0x00000001之类的值加密得到的,因此还是只得一试的 之后还有0x7E、0x1E的出现频率比较高,但不连续,如果0x5A失败的话也可以试一试~

(实在不行也可以手动爆破,好几个交流的朋友都是一个一个试的,甚至还有一个小可爱是从255倒着试的23333333333333333333333333)

0x5A对应十进制的90,即NOP,这个数字还是比较有意义的233 输入以后便回显了flag(cin接受的是int类型,因此直接输入数字即可,而不用chr)

我们继续解密查看一下代码 解密IDC脚本

记一次CTF比赛中,使用python对exe程序中交互爆破flag-Alkaid

还是挺简单的,后面字节直接拷贝,前4个字节异或解密覆盖上去就完了

以上是WP里的分析部分,其中手动爆破实在是太费时间了,我们完全可以构建一个python脚本来帮我们进行爆破:

首先查阅资料,发现可以使用Popen方法,以下是收集到的资料:

使用Popen模块产生新的process

现在大部分人都喜欢使用Popen。Popen方法不会打印出cmd在linux上执行的信息。的确,Popen非常强大,支持多种参数和模式。使用前需要from subprocess import Popen, PIPE。但是Popen函数有一个缺陷,就是它是一个阻塞的方法。如果运行cmd时产生的内容非常多,函数非常容易阻塞住。解决办法是不使用wait()方法,但是也不能获得执行的返回值了。

Popen原型是:

参数bufsize:指定缓冲。我到现在还不清楚这个参数的具体含义,望各个大牛指点。

参数executable用于指定可执行程序。一般情况下我们通过args参数来设置所要运行的程序。如果将参数shell设为 True,executable将指定程序使用的shell。在windows平台下,默认的shell由COMSPEC环境变量来指定。

参数stdin, stdout, stderr分别表示程序的标准输入、输出、错误句柄。他们可以是PIPE,文件描述符或文件对象,也可以设置为None,表示从父进程继承。

参数preexec_fn只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用。

参数Close_sfs:在windows平台下,如果close_fds被设置为True,则新创建的子进程将不会继承父进程的输入、输出、错误管 道。我们不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)。

如果参数shell设为true,程序将通过shell来执行。

参数cwd用于设置子进程的当前目录。

参数env是字典类型,用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。

参数Universal_newlines:不同操作系统下,文本的换行符是不一样的。如:windows下用’/r/n’表示换,而Linux下用 ‘/n’。如果将此参数设置为True,Python统一把这些换行符当作’/n’来处理。

参数startupinfo与createionflags只在windows下用效,它们将被传递给底层的CreateProcess()函数,用 于设置子进程的一些属性,如:主窗口的外观,进程的优先级等等。

subprocess.PIPE
在创建Popen对象时,subprocess.PIPE可以初始化stdin, stdout或stderr参数,表示与子进程通信的标准流。

subprocess.STDOUT
创建Popen对象时,用于初始化stderr参数,表示将错误通过标准输出流输出。

Popen的方法:

Popen.poll()
用于检查子进程是否已经结束。设置并返回returncode属性。

Popen.wait()
等待子进程结束。设置并返回returncode属性。

Popen.communicate(input=None)
与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。 Communicate()返回一个元组:(stdoutdata, stderrdata)。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如 果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。

Popen.send_signal(signal)
向子进程发送信号。

Popen.terminate()
停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。

Popen.kill()
杀死子进程。

Popen.stdin
如果在创建Popen对象是,参数stdin被设置为PIPE,Popen.stdin将返回一个文件对象用于策子进程发送指令。否则返回None。

Popen.stdout
如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回 None。

Popen.stderr
如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回 None。

Popen.pid
获取子进程的进程ID。

Popen.returncode
获取进程的返回值。如果进程还没有结束,返回None。

 

有了这些我们就可以去写一个python爆破脚本了,代码如下:

结果如图所示:

记一次CTF比赛中,使用python对exe程序中交互爆破flag-Alkaid