在”深度优先“的学习过程中发现了pin这个工具,之前对于平坦化的程序只会用deflat脚本去平坦化,去不了就只能苦逼的硬调了,直到发现了pin这个工具,打开了侧信道的大门,然后也在搜学习资料的时候搜到了比较轻量的perf工具
比较典型的题目有2019 DDCTF 的confused,这里主要拿自己出的一道题目讲一下用法。
安装 pin 下载地址:https://software.intel.com/content/www/us/en/develop/articles/pin-a-binary-instrumentation-tool-downloads.html
下载对应系统的pin即可
1 2 3 4 5 6 wget -c https://software.intel.com/sites/landingpage/pintool/downloads/pin-3.13-98189-g60a6ef199-gcc-linux.tar.gz tar -xvf pin-3.13-98189-g60a6ef199-gcc-linux.tar.gz mv pin-3.13-98189-g60a6ef199-gcc-linux pin cd pin/source/tools/ManualExamples/ make all TAEGET=intel64 make all TAEGET=ia32
perf ubuntu 16.04, 内核为4.4.0-177
1 2 3 apt install linux-tools-common apt install linux-tools-4.4.0-177-generic apt install linux-cloud-tools-4.4.0-177-generic
使用 侧信道的攻击主要是根据程序执行的指令数目进行攻击,因此只演示指令计数的使用方法
pin pintools的使用为
1 path/to/pin -t path/to/your/pintool -- path/to/binary <arg>
举例来说,在我这里使用pin如下:
1 2 3 4 5 6 $ ./pin/pin -t ./pin/source/tools/ManualExamples/obj-intel64/inscount0.so -- ./Just_reverse_it Input your flag: 12 Sorry $ cat inscount.out Count 1941819
这里的Count就是从程序开始执行到退出所执行的指令数目了
perf 1 2 3 4 5 $ perf stat -x : -e instructions:u ./Just_reverse_it Input your flag: 12 Sorry 1941433::instructions:u:1330133:100.00
这里的1941433就是perf统计指令执行的指令数目了
例题 二进制文件:Just_reverse_it
题目是一道简单的虚拟机题,逻辑如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # data[i]=0x12^data[i-1]^(input[i]-i) loop: mov reg1,input[reg3] dec reg1,reg3 xor reg2,reg1 mov reg1,0xCD xor reg1,reg2 cmp reg1,data[i] mov reg2,reg1 jz continue return 0 continue: inc reg3 cmp reg3,const jb loop return 1
可以看到当其中一个字节输入错误后,就会立刻跳出,而输入正确就会进入下一个字节的运算和比较,因此正确和错误,执行的指令数的差别,就能让我们使用侧信道的攻击方法了。
pin exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import osimport sysimport subprocessclass Shell (object) : def runCmd (self, cmd) : res = subprocess.Popen(cmd, shell=True , stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) sout, serr = res.communicate() return res.returncode, sout, serr, res.pid def initPin (self, cmd) : res = subprocess.Popen(cmd, shell=True , stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) self.res = res def pinWrite (self, input) : self.res.stdin.write(input) def pinRun (self) : sout, serr = self.res.communicate() return sout, serr cmd = "~/pin/pin -t ~/pin/source/tools/ManualExamples/obj-intel64/inscount0.so -- ./Just_reverse_it" shell = Shell() s = "" import stringchs=string.printable for i in range(48 ): max_num = 0 max_ch = "" for ch in chs: tmp = s + ch +(48 -len(s)-1 )*'a' shell.initPin(cmd) shell.pinWrite(tmp) sout,serr = shell.pinRun() with open('inscount.out' ) as f: count = f.readline().split(' ' )[1 ] count = int(count) if (count>max_num): max_num = count max_ch = ch s+=max_ch print(s)
perf exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import osimport sysimport subprocessclass Shell (object) : def runCmd (self, cmd) : res = subprocess.Popen(cmd, shell=True , stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) sout, serr = res.communicate() return res.returncode, sout, serr, res.pid def initPin (self, cmd) : res = subprocess.Popen(cmd, shell=True , stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) self.res = res def pinWrite (self, input) : self.res.stdin.write(input) def pinRun (self) : sout, serr = self.res.communicate() return sout, serr cmd = 'perf stat -x : -e instructions:u ./Just_reverse_it' shell = Shell() s = "" import stringchs=string.printable for i in range(48 ): max_num = 0 max_ch = "" for ch in chs: tmp = s + ch +(48 -len(s)-1 )*'a' shell.initPin(cmd) shell.pinWrite(tmp) sout,serr = shell.pinRun() sout = sout.split('\n' )[2 ] count = int(sout.split('::' )[0 ]) if (count>max_num): max_num = count max_ch = ch s+=max_ch print(s)
总结 侧信道的好处在于只需要进行简单的分析,剩下的躺着拿flag就行了,但对于一些指令数目差距不大的题目,侧信道也并不是那么简单,需要自己找一下规律或者改写自己的pin工具。反正我现在碰到简单输入flag的逆向就想先侧一侧或者angr一下。
总之能用最好,不能用也不亏,花不了很多时间。
参考资料