巅峰极客比赛中逆向第二题:Input you lucky number
这道题首先我们使用IDA分析:
main函数中通过cin接收输入,下面做了一大堆操作 但是通过对input变量的跟踪,可以发现都没有什么卵用 与输入无关,因此我们根本不需要去读,反正都是正向操作
只有箭头所指的部分才需要关注
于是查看sub_401100
v3 = _mm_cvtsi32_si128(input); v4 = _mm_unpacklo_epi8(v3, v3); v5 = _mm_shuffle_epi32(_mm_unpacklo_epi16(v4, v4), 0);
最开头这三个处理非常丑,要不挨个去查手册,要不就直接动调观察 汇编指令是三条
punpcklbw xmm0, xmm0 punpcklwd xmm0, xmm0 pshufd xmm1, xmm0, 0
全部是交叉赋值的,分别将1字节的input变为2字节、4字节、16字节
(试了OD和IDA都看不到XMM寄存器的值,最后用x32dbg才成功跟踪到) 下面两段循环实际上都是逐字节异或 因为第一段以XMM寄存器来操作,单位必须是16个字节,对于零头就无能为力了,于是使用第二段循环来逐字节操作 不知道是出题人故意设置的干扰还是编译器为了加速而优化出来的23333
那么下一个问题就是key啦 回头看参数a2对应的值,是0x401000+v6处的代码 观察加密后的HEX
(动调可知起始字节在0x89之后)
这里有大量的连续0x5A出现,在PE文件中通常会有大量的0x00作为填充,这里由于加密字节数太少所以没有用到上述的论据 不过连续三个0x5A通常一般是0x00000001之类的值加密得到的,因此还是只得一试的 之后还有0x7E、0x1E的出现频率比较高,但不连续,如果0x5A失败的话也可以试一试~
(实在不行也可以手动爆破,好几个交流的朋友都是一个一个试的,甚至还有一个小可爱是从255倒着试的23333333333333333333333333)
0x5A对应十进制的90,即NOP,这个数字还是比较有意义的233 输入以后便回显了flag(cin接受的是int类型,因此直接输入数字即可,而不用chr)
我们继续解密查看一下代码 解密IDC脚本
auto i;for(i=0;i<0x61;i++){PatchByte(0x401013+i, Byte(0x401013+i)^90);}
还是挺简单的,后面字节直接拷贝,前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爆破脚本了,代码如下:
import subprocess pname='C:/Users/49397/Desktop/1.exe' for i in range(90,100): i=str(i).encode() p=subprocess.Popen(pname,stdin=subprocess.PIPE,stdout=subprocess.PIPE) result=p.communicate(input=i) res=result[0].decode()[0:] if 'flag' in res: print (res) break
结果如图所示:
info 评论功能已经关闭了呐!