1625-5 王子昂 总结《2018年2月4日》 【连续第492天总结】
A. Python多线程调用PinTools
B.
前言
本文初衷是为了拟补PinTools在爆破中效率较低的问题,而非PinTools针对多线程程序的相关描述
为了不引起期望获得后者的读者们的误会先行提出
思路
结果读取
之前参照Invicsfate一步一步修改来的爆破脚本原理是将待输入字符写入一个文件中,然后通过重定向来节省向管道写入的时间和麻烦
这样只需要读取回显即可
另一方面,inscount例程的输出方式都是写出文件,因此之前的脚本也只能再读取结果文件
对于这样的脚本,多线程修改的弊端在于输出方式。
输入方式可以通过每个线程使用独立的文件来重定向结局,
但结果最后都输出到同一个文件中,因此多线程并行的时候势必会彼此干扰
因此首先要找到一个可以使得多线程区分开的结果输出方式
刚开始想得是自定义命令行,与输入的方式类似,使每个线程的输出都放在独立的一个结果文件中。找了一下API只看到说在KNOB中,但是没有找到怎么使用
后来想了一下,既然是通过ostream输出到文件,当然也可以用cout来打印到屏幕中吧。因为每个线程都独立开了一个管道,所以是满足互不影响需求的
尝试了一下果然可以
参数传入
下一步是思考怎么进行线程间的通信。传递参数很简单,但是结果回收和统计就稍微有点意思了。
最理想的状况当然是送给子线程一个指针,然后子线程修改指针对应的值,达到引用传递的效果。然而python中并没有指针的说法
但Python中也并不是值传递,而是传对象引用
的形式
在Python中,万物皆对象
无论是常数(1、2、3)还是字符串、列表、元组等等,它们都是对象
而对象就涉及到可变对象和不可变对象了
可变对象仅有列表和字典,其他都是不可变对象
可变对象意味着子元素可以修改,而对象不变。
eg:
list = [1, 2]
list[0] = 2
修改前后list指向的是同一个列表对象
而字符串、元组则不是
s = "123"
s[0] = "2"
->Error
s = "223"
s += "1"
它们不能修改元素的对象,虽然可以重新赋值或是通过一些内建方法来修改,但是这样修改完的字符串/元组实际上是一个新的对象,可以通过id()来验证
回到需求本身,如果传一个可变对象的引用过去,那么函数中是可以修改这个可变对象的,从而达到引用传递的效果
脚本写起来不是太复杂
代码
#coding=utf-8
import popen2
import string
import threading
import os
from time import time
CMD = r"E:\ctf\pin\pin.exe -t E:\ctf\pin\source\tools\Inscount0_win\obj-ia32\inscount0_win_stdout.dll -- F:\ctf\Whale\CrackMe\CrackMe.exe <"
choices = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&'()*+,-./:;<=>?@[\]^_`{|}~"
flag = ""
def writefile(data, file):
fi = open(file,'w')
fi.write(data)
fi.close()
def execlCommand(command):
fin,fout = popen2.popen2(command)
for i in range(3):fin.readline()
p = int(fin.readline())
return p
def main(tmp, i):
key = flag + i # 测试字符串
# print(">", key)
writefile(key, str(ord(i)))
result = execlCommand(CMD + str(ord(i)))
print("%s : %d" % (i, result))
tmp[result] = i
def test(command):
fin,fout = popen2.popen2(command)
(fin.readline())
s = fin.readline()
print(s)
if("Failed" in s):
return True
NUM_threads = 5
time1 = time()
while(1):
tmp = {}
for j in range(len(choices)//NUM_threads + 1):
threads = []
for i in range(NUM_threads):
try:
t = threading.Thread(target=main, args=(tmp, choices[j*NUM_threads+i]))
except IndexError:
break
t.start()
threads.append(t)
for i in threads:
i.join()
p = sorted(tmp.keys(), reverse=True)[0]
if(tmp.keys().count(p) != len(tmp)):break
p = tmp[p]
flag += p
print(flag)
if(test(CMD + str(ord(p)))):break
print("总用时:%ds"%(time()-time1))
for i in choices:
os.remove('./' + str(ord(i)))
后记
然鹅。。。
效率过低的原因看来是PinTools本身吃光了CPU的资源,多线程并不能提高效率,甚至还会拉低一些。
单线程情况下平均每个字符需要2s左右,多线程(10个线程情况下)则需要20-30s。
于是多线程提高效率的方案就夭折了
下一步尝试Frida或者其他方法来Hook,还有Trace+BBL级别的使用尝试
C. 明日计划
PinTools-Trace\Bbl