Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create hopper_search_block.py #10

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 214 additions & 0 deletions search_oc_block/hopper_search_block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import types
import os
import operator
import csv
import sys
import json
import traceback

doc = Document.getCurrentDocument()

def alert(val):
if type(val) is types.LongType:
doc.message(hex(val), ['OK'])
else:
doc.message(str(val), ['OK'])

def log(val):
if type(val) is types.LongType:
doc.log(hex(val) + '\n')
else:
doc.log(str(val) + '\n')

TextSeg = doc.getSegmentByName('__TEXT')
DataSeg = doc.getSegmentByName('__DATA')
SymbolSeg = doc.getSegmentByName('External Symbols')

IS32BIT = not doc.is64Bits()

doc_entry = doc.getEntryPoint()

IS_MAC = 'x86_64' in Instruction.stringForArchitecture(TextSeg.getInstructionAtAddress(doc_entry).getArchitecture())


log("Start analyze binary for " + ("Mac" if IS_MAC else "iOS"))

def isInText(x):
return doc.getSegmentAtAddress(x).getName() == '__TEXT'

def isInData(x):
return doc.getSegmentAtAddress(x).getName() == '__DATA'

GlobalBlockAddr = doc.getAddressForName("__NSConcreteGlobalBlock")


class GlobalBlockInfo:
pass

AllGlobalBlockMap = {}


def funcIsGlobalBlockFunc(block_func):
return block_func in AllGlobalBlockMap

def isPossibleStackBlockForFunc(block_func):
if not isInText(block_func):
return False

proc = TextSeg.getProcedureAtAddress(block_func)
if not proc or proc.getEntryPoint() != (block_func & ~ 1):
return False

#block addr cannot be called directly
refsTo = TextSeg.getReferencesOfAddress(block_func)
codeRefs = []
if IS_MAC:
codeRefs = filter(lambda x: TextSeg.getInstructionAtAddress(x).getInstructionString() == 'call', refsTo)
else:
codeRefs = filter(lambda x: TextSeg.getInstructionAtAddress(x).getInstructionString() == 'bl', refsTo)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

此处判断 最好用getTypeAtAddress来写,现在这个写法会出错,因为oc的class_method的那些结构体也在Text Segment里面 ,直接取instrument会出错的,因为那个地址可能并不是指令,而是Text段里的只读数据

if len(codeRefs) !=0 :
return False

# block func should be ref in only 1 function
superFuncs = []
for ref in refsTo:
proc = TextSeg.getProcedureAtAddress(ref)
if not proc:
continue
superFuncs.append(proc.getEntryPoint())
superFuncs = list (set (superFuncs))
if len(superFuncs) != 1:
# print '%x is not block because be not ref from 1 function' % block_func
return False

return True

def superFuncForStackBlock(block_func):
refsTo = TextSeg.getReferencesOfAddress(block_func)
superFuncs = [TextSeg.getProcedureAtAddress(x).getEntryPoint() for x in refsTo]
superFuncs = list (set (superFuncs))
if len(superFuncs) != 1:
return None
super_func_addr = superFuncs[0]
if IS_MAC:
return super_func_addr
else:
return super_func_addr | TextSeg.isThumbAtAddress(block_func) # thumb
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我这里 hopper 4.0.8 调用 TextSeg.isThumbAtAddress(block_func) 会经常crash,不是python脚本出异常,而是整个hopper进程crash,怀疑hopper有bug,不知道你那边的环境是什么,
考虑把所有的 “| isThumbAtAddress”都去掉 检查一下结果对不对,我记得这里带上 thumb标志位,只是为了内部统一,理论上这里去掉,其他地方处理好下,应该不影响最后结果,检查下最后结果对不对就好



def superFuncForBlockFunc(block_func):
if funcIsGlobalBlockFunc(block_func):
return AllGlobalBlockMap[block_func].superFunc

superStackFunc = superFuncForStackBlock(block_func)
return superStackFunc # maybe None

resultDict = {}


def findBlockName(block_func):
# print "find block name %X" % block_func
proc = TextSeg.getProcedureAtAddress(block_func)
if not proc:
return ""
funcName = doc.getNameAtAddress(proc.getEntryPoint())

if len(funcName) != 0 and funcName[0] in ('-', '+'):
return funcName

# maybe nested block
superBlockFuncAddr = superFuncForBlockFunc(block_func)
if superBlockFuncAddr == None:
return "";
if not IS_MAC:
superBlockFuncAddr = superBlockFuncAddr | TextSeg.isThumbAtAddress(superBlockFuncAddr) # thumb
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同上面的isThumbAtAddress


superBlockName = findBlockName(superBlockFuncAddr)

if len(superBlockName) == 0:
return ""
else:
return superBlockName + "_block"

def main():
for struct in SymbolSeg.getReferencesOfAddress(GlobalBlockAddr):
func = 0L
FUNC_OFFSET_IN_BLOCK = 12 if IS32BIT else 16
if IS32BIT:
func = DataSeg.readUInt32LE(struct + FUNC_OFFSET_IN_BLOCK)
else:
func = DataSeg.readUInt64LE(struct + FUNC_OFFSET_IN_BLOCK)


info = GlobalBlockInfo()
info.func = func
info.struct = struct
if len(DataSeg.getReferencesOfAddress(struct)) == 0:
continue
refTo = DataSeg.getReferencesOfAddress(struct)[0]
if isInText(refTo):
info.superFunc = TextSeg.getProcedureAtAddress(refTo).getEntryPoint()
elif isInData(refTo):
ref = DataSeg.getReferencesOfAddress(refTo)[0]
info.superFunc = TextSeg.getProcedureAtAddress(ref).getEntryPoint()

AllGlobalBlockMap[func] = info

#find all possible Stack Block
allPossibleStackBlockFunc = []
allRefToBlock=[]
StackBlockAddr = doc.getAddressForName("__NSConcreteStackBlock")
# if IS32BIT:
allRefToBlock = DataSeg.getReferencesOfAddress(SymbolSeg.getReferencesOfAddress(StackBlockAddr)[0])
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个__NSConcreteStackBlock符号地址选取的不太对,你想要的那个符号不一定能在第一个,考虑下用下面这段代码,我这里简单测试了下,应该是可以的,也可能还有问题,
我觉得你应该是先想到了下面这个写法,但是你最后没有采用,可能是有我没有测到的问题,这个可以再详细交流下
StackBlockAddr = doc.getAddressForName("__NSConcreteStackBlock")
allRefToBlock = SymbolSeg.getReferencesOfAddress(StackBlockAddr)
allRefToBlock = filter(lambda x:isInText(x), allRefToBlock)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

所以这个事情目前还是没有结论呀?

allRefToBlock = filter(lambda x:isInText(x), allRefToBlock)

for addr in allRefToBlock:
proc = TextSeg.getProcedureAtAddress(addr)
if not proc:
continue
LineNumAround = 30 #Around 30 instruction
instr_size = 15 if IS_MAC else 4
scan_addr_min = max (addr - LineNumAround * instr_size, proc.getEntryPoint())
scan_addr_max = min (addr + LineNumAround * instr_size, proc.getBasicBlockAtAddress(addr).getEndingAddress())
for scan_addr in range(scan_addr_min, scan_addr_max):
allPossibleStackBlockFunc += TextSeg.getReferencesFromAddress(scan_addr) # all function pointer used around __NSConcreteStackBlock

allPossibleStackBlockFunc = list (set (allPossibleStackBlockFunc))

allPossibleStackBlockFunc = filter(lambda x:isPossibleStackBlockForFunc(x) , allPossibleStackBlockFunc )


#process all Global Block
for block_func in AllGlobalBlockMap:
block_name = findBlockName(block_func)
resultDict[block_func] = block_name

for block_func in allPossibleStackBlockFunc:
block_name = findBlockName(block_func)
resultDict[block_func] = block_name

log('Success!')
output_file = doc.askFile('~/', 'block_symbol.json', 'save')
list_output = []
error_num = 0
for addr in resultDict:
name = resultDict[addr]
if len(name) == 0 or name[0] not in ('-', '+'):
error_num += 1
continue

list_output += [{"address":("0x%X" % addr), "name":name}]

encodeJson = json.dumps(list_output, indent=1)
f = open(output_file if output_file else "~/block_symbol.json" , "w")
f.write(encodeJson)
f.close()
log('\nrestore block num %d ' % len(list_output))
log('\norigin block num: %d(GlobalBlock: %d, StackBlock: %d)' % (len(allRefToBlock) + len(AllGlobalBlockMap), len(AllGlobalBlockMap), len(allRefToBlock)))


try:
main()
log('Done!')
except Exception as e:
log('-----------------\n\033[1;31mException Occured For:\033[0m\n' + str(e) + '\nStack Info:\n' + traceback.format_exc() + '\n------------------')