使用pin解决CTF的部分逆向题

最近两天又在折腾pin,是一个很强大的二进制程序的插桩分析工具,这里我就只用了inscount这个pintool,其他的以后再慢慢研究,inscount就是一个可以计算执行指令数的一个程序,通过所获的指令数分析我们可以来对程序爆破出正确的输入

pin的安装及使用

大家可以去看ctf-wiki上的教程,我也是跟着教程复现了几道题

pin in ctf

NDH2K13-crackme-500

首先这道题ida一打开就是各种报错,所以我们粗暴一点直接上pin试试,由于wiki已经讲的很详细了,这里我就直接上脚本了,我下的pin3.7的不知道为什么无法输出结果到控制台(如果有大佬知道怎么修改可以输出到控制台可以告诉我!),所以对wiki上的脚本做了一点修改,首先猜测输入的长度(脚本里的myinscount1.so其实就是inscount1.so)

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from subprocess import Popen, PIPE
from sys import argv
import string
import time

pinPath = "/home/hacker_mao/pin-3.7/pin"
pinInit = lambda tool, elf: Popen([pinPath, '-t', tool, '--', elf], stdin = PIPE, stdout = PIPE)
pinWrite = lambda cont: pin.stdin.write(cont)
#pinRead = lambda : open('./myinscount1.out','r').read().strip('\n')
def pinRead():
#f = open('./inscount.out','r')
f = open('./myinscount1.out','r')
file = f.read().strip('\n')
f.close()
return file

if __name__ == "__main__":
last = 0
for i in xrange(1, 30):
#pin = pinInit("./inscount0.so", "./crackme")
pin = pinInit("./myinscount1.so", "./crackme")
pinWrite("a" * i + '\n')
time.sleep(0.5)
#print pinRead()
now = int(pinRead().split("Count ")[1])

print "inputLen({:2d}) -> ins({}) -> delta({})".format(i, now, now - last)
last = now
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
☁  obj-intel64  python guessLen.py 
inputLen( 1) -> ins(161503) -> delta(161503)
inputLen( 2) -> ins(164299) -> delta(2796)
inputLen( 3) -> ins(167095) -> delta(2796)
inputLen( 4) -> ins(169891) -> delta(2796)
inputLen( 5) -> ins(172687) -> delta(2796)
inputLen( 6) -> ins(175483) -> delta(2796)
inputLen( 7) -> ins(178279) -> delta(2796)
inputLen( 8) -> ins(184000) -> delta(5721)
inputLen( 9) -> ins(183872) -> delta(-128)
inputLen(10) -> ins(186668) -> delta(2796)
inputLen(11) -> ins(189464) -> delta(2796)
inputLen(12) -> ins(192260) -> delta(2796)
inputLen(13) -> ins(195056) -> delta(2796)

#可以看到输入长度为8的时候指令变化较大,所以我们要输入东西的长度很大可能是8

接下来爆破内容

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
47
48
49
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from subprocess import Popen, PIPE
from sys import argv
import string
import pdb
import time

pinPath = "/home/hacker_mao/pin-3.7/pin"
pinInit = lambda tool, elf: Popen([pinPath, '-t', tool, '--', elf], stdin = PIPE, stdout = PIPE)
pinWrite = lambda cont: pin.stdin.write(cont)
#pinRead = lambda : pin.communicate()[0]
def pinRead():
#f = open('./inscount.out','r')
f = open('./myinscount1.out','r')
file = f.read().strip('\n')
f.close()
return file

if __name__ == "__main__":
last = 0
# dic = map(chr, xrange(0x20, 0x80))
dic = string.ascii_letters + string.digits + "+_ "
pwd = '?' * 8
dicIdx = 0
pwdIdx = 0

while True:
pwd = pwd[: pwdIdx] + dic[dicIdx] + pwd[pwdIdx + 1: ]
# pdb.set_trace()
pin = pinInit("./myinscount1.so", "./crackme")
pinWrite(pwd + '\n')
time.sleep(1)

now = int(pinRead().split("Count ")[1])

print "input({}) -> now({}) -> delta({})".format(pwd, now, now - last)

if now - last > 2000 and dicIdx:
pwdIdx += 1
dicIdx = -1
last = 0
if pwdIdx >= len(pwd):
print "Found pwd: {}".format(pwd)
break

dicIdx += 1
last = now
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
☁  obj-intel64  python guessPWD.py 
input(a???????) -> now(184000) -> delta(184000)
input(b???????) -> now(184000) -> delta(0)
input(c???????) -> now(184000) -> delta(0)
input(d???????) -> now(184000) -> delta(0)
input(e???????) -> now(184000) -> delta(0)
...
input(x???????) -> now(184000) -> delta(0)
input(y???????) -> now(184000) -> delta(0)
input(z???????) -> now(184000) -> delta(0)
input(A???????) -> now(188037) -> delta(4037)
input(Aa??????) -> now(188037) -> delta(0)
input(Ab??????) -> now(188037) -> delta(0)
...
input(AzI0wBsQ) -> now(212265) -> delta(0)
input(AzI0wBsR) -> now(212265) -> delta(0)
input(AzI0wBsS) -> now(212265) -> delta(0)
input(AzI0wBsT) -> now(212265) -> delta(0)
input(AzI0wBsU) -> now(212265) -> delta(0)
input(AzI0wBsV) -> now(212265) -> delta(0)
input(AzI0wBsW) -> now(212265) -> delta(0)
input(AzI0wBsX) -> now(216172) -> delta(3907)
Found pwd: AzI0wBsX

☁ obj-intel64 ./crackme
Jonathan Salwan loves you <3
----------------------------

Password: AzI0wBsX
Good password

很快我们就轻松得得到了pwd

QCTF-ollvm

main

这题ida打开也挺复杂的,但是告诉了我们输入的长度要是38,所以我们也尝试一下用pin,但是一开始我在ubuntu无法运行这道题

1
2
3
4
5
6
7
8
9
☁  ollvm  ldd ollvm                                                            
linux-vdso.so.1 => (0x00007ffebf98c000)
libgmpxx.so.4 => not found
libgmp.so.10 => /usr/lib/x86_64-linux-gnu/libgmp.so.10 (0x00007fa01c9f6000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fa01c674000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa01c36a000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa01c154000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa01bd8a000)
/lib64/ld-linux-x86-64.so.2 (0x00005642fe7ab000)

上网找了一下,发现是我的linux没有安装gmp,于是先装个gmp

安装gmp

  1. 下载最新gmp [ gmp_6.1.2+dfsg.orig.tar.xz],解压(https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/gmp/2:6.1.2+dfsg-2/gmp_6.1.2+dfsg.orig.tar.xz)

  2. 安装gmp

    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
    #安装m4,不然会报错
    sudo apt-get install build-essential m4

    # 编译准备
    ./configure --prefix=/usr \
    --enable-cxx \
    --disable-static \
    --docdir=/usr/share/doc/gmp-6.1.2

    # 编译包和生成HTML文档
    make
    make html
    make check

    # 安装包和相关文档
    sudo make install
    make install-html

    # 然后我们的ollvm文件就能正常运行了
    ☁ ollvm ldd ollvm
    linux-vdso.so.1 => (0x00007ffebf98c000)
    libgmpxx.so.4 => /usr/lib/libgmpxx.so.4 (0x00007fa01cc76000)
    libgmp.so.10 => /usr/lib/x86_64-linux-gnu/libgmp.so.10 (0x00007fa01c9f6000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fa01c674000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa01c36a000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa01c154000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa01bd8a000)
    /lib64/ld-linux-x86-64.so.2 (0x00005642fe7ab000)

    ☁ ollvm ./ollvm asdsda
    flag require 38 chars

然后就可以上我们的脚本了

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
47
48
49
50
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from subprocess import Popen, PIPE
from sys import argv
import string
import pdb
import time

pinPath = "/home/hacker_mao/pin-3.7/pin"
pinInit = lambda tool, elf, flag: Popen([pinPath, '-t', tool, '--', elf, flag] , stdout = PIPE)
pinWrite = lambda cont: pin.stdin.write(cont)
#pinRead = lambda : pin.communicate()[0]
def pinRead():
f = open('./inscount.out','r')
#f = open('./myinscount1.out','r')
file = f.read().strip('\n')
f.close()
return file

if __name__ == "__main__":
last = 0
# dic = map(chr, xrange(0x20, 0x80))
dic = string.ascii_letters + string.digits + "+_ {}"
pwd = '_' * 38
dicIdx = 0
pwdIdx = 0

while True:
pwd = pwd[: pwdIdx] + dic[dicIdx] + pwd[pwdIdx + 1: ]
# pdb.set_trace()
#pin = pinInit("./myinscount1.so", "./ollvm")
pin = pinInit("./inscount0.so", "./ollvm",pwd)
#pinWrite(pwd + '\n')
time.sleep(5)
now = int(pinRead().split("Count ")[1])

print "input({}) -> now({}) -> delta({})".format(pwd, now, now - last)

#if now - last > 2000 and dicIdx:
if now - last > 2000 and now - last < 6027776:
pwdIdx += 1
dicIdx = -1
last = 0
if pwdIdx >= len(pwd):
print "Found pwd: {}".format(pwd)
break

dicIdx += 1
last = now
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
☁  ollvm  python guessPWD.py
input(a_____________________________________) -> now(6027776) -> delta(6027776)
input(b_____________________________________) -> now(6027801) -> delta(25)
input(c_____________________________________) -> now(6027235) -> delta(-566)
...
input(P_____________________________________) -> now(6027750) -> delta(504)
input(Q_____________________________________) -> now(7326495) -> delta(1298745)
input(Qa____________________________________) -> now(7327120) -> delta(625)
input(Qb____________________________________) -> now(7327070) -> delta(-50)
...
input(QCTF{5Ym4aOEww2NcZcvUPOWKYMnPaqPywR2m8) -> now(54119926) -> delta(30)
input(QCTF{5Ym4aOEww2NcZcvUPOWKYMnPaqPywR2m9) -> now(54119867) -> delta(-59)
input(QCTF{5Ym4aOEww2NcZcvUPOWKYMnPaqPywR2m+) -> now(54119910) -> delta(43)
input(QCTF{5Ym4aOEww2NcZcvUPOWKYMnPaqPywR2m_) -> now(54119894) -> delta(-16)
input(QCTF{5Ym4aOEww2NcZcvUPOWKYMnPaqPywR2m ) -> now(54119908) -> delta(14)
input(QCTF{5Ym4aOEww2NcZcvUPOWKYMnPaqPywR2m{) -> now(54119875) -> delta(-33)
input(QCTF{5Ym4aOEww2NcZcvUPOWKYMnPaqPywR2m}) -> now(54120465) -> delta(590)
#最后一位可能需要自己处理一下

参考文章:

  1. https://blog.csdn.net/win_in_action/article/details/46956245
  2. https://blog.csdn.net/shengerjianku/article/details/80721510
  3. https://blog.csdn.net/liuruiqun/article/details/49590531#
  4. https://github.com/bash-c/pin-in-CTF
  5. https://ctf-wiki.github.io/ctf-tools/binary_core_tools/instrumentation/intel_pin/#pin