-
Notifications
You must be signed in to change notification settings - Fork 366
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
eugeneyang
authored and
eugeneyang
committed
Aug 27, 2016
1 parent
5c86d83
commit f49cbbf
Showing
8 changed files
with
272 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,52 @@ | ||
# restore-symbol | ||
A tool to restore symbol table for iOS app. | ||
|
||
Example: restore symbol for Alipay | ||
![](https://raw.githubusercontent.com/tobefuturer/restore-symbol/master/picture/after_restore.jpeg) | ||
|
||
|
||
## How to make | ||
``` | ||
git clone --recursive https://github.com/tobefuturer/restore-symbol.git | ||
cd restore-symbol && make | ||
./restore-symbol | ||
``` | ||
|
||
## How to use | ||
- 1. Scan all oc method using class-dump. | ||
- 2. Search block symbol in IDA to get json symbol file, using script([`search_oc_block/ida_search_block.py`](https://github.com/tobefuturer/restore-symbol/blob/master/search_oc_block/ida_search_block.py)) . | ||
|
||
![](http://blog.imjun.net/2016/08/25/iOS%E7%AC%A6%E5%8F%B7%E8%A1%A8%E6%81%A2%E5%A4%8D-%E9%80%86%E5%90%91%E6%94%AF%E4%BB%98%E5%AE%9D/ida_result_position.png) | ||
|
||
![](http://blog.imjun.net/2016/08/25/iOS%E7%AC%A6%E5%8F%B7%E8%A1%A8%E6%81%A2%E5%A4%8D-%E9%80%86%E5%90%91%E6%94%AF%E4%BB%98%E5%AE%9D/ida_result_sample.jpg) | ||
|
||
- 3. Use restore-symbol to inject symbols into mach o file. | ||
``` | ||
./restore-symbol ./origin_AlipayWallet -o ./AlipayWallet_with_symbol -j block_symbol.json | ||
``` | ||
|
||
|
||
## Command Line Usage | ||
``` | ||
Usage: restore-symbol -o <output-file> [-j <json-symbol-file>] <mach-o-file> | ||
where options are: | ||
-o <output-file> New mach-o-file path | ||
--disable-oc-detect Disable auto detect and add oc method into symbol table, | ||
only add symbol in json file | ||
-j <json-symbol-file> Json file containing extra symbol info, the key is "name","address" | ||
like this: | ||
[ | ||
{ | ||
"name": "main", | ||
"address": "0xXXXXXX" | ||
}, | ||
{ | ||
"name": "-[XXXX XXXXX]", | ||
"address": "0xXXXXXX" | ||
}, | ||
.... | ||
] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,14 @@ | ||
|
||
.PHONY:restore-symbol | ||
|
||
TMP_FILE := libMachObjC.a restore-symbol.dSYM/ build/ | ||
|
||
restore-symbol: | ||
rm restore-symbol | ||
rm -f restore-symbol | ||
xcodebuild -project "restore-symbol.xcodeproj" -target "restore-symbol" -configuration "Release" CONFIGURATION_BUILD_DIR="$(shell pwd)" -jobs 4 build | ||
rm -rf libMachObjC.a restore-symbol.dSYM/ | ||
rm -rf $(TMP_FILE) | ||
|
||
|
||
clean: | ||
rm -rf restore-symbol libMachObjC.a restore-symbol.dSYM/ | ||
rm -rf restore-symbol $(TMP_FILE) | ||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
import idautils | ||
import idc | ||
from idaapi import PluginForm | ||
import operator | ||
import csv | ||
import sys | ||
import json | ||
|
||
|
||
IS32BIT = not idaapi.get_inf_structure().is_64bit() | ||
|
||
|
||
|
||
def isInText(x): | ||
return SegName(x) == '__text' | ||
|
||
|
||
GlobalBlockAddr = LocByName("__NSConcreteGlobalBlock") | ||
|
||
class GlobalBlockInfo: | ||
pass | ||
|
||
AllGlobalBlockMap = {} | ||
for struct in list(DataRefsTo(GlobalBlockAddr)): | ||
func = 0L | ||
FUNC_OFFSET_IN_BLOCK = 12 if IS32BIT else 16 | ||
if IS32BIT: | ||
func = Dword(struct + FUNC_OFFSET_IN_BLOCK) | ||
else: | ||
func = Qword(struct + FUNC_OFFSET_IN_BLOCK) | ||
|
||
|
||
info = GlobalBlockInfo() | ||
info.func = func | ||
info.struct = struct | ||
if len(list(DataRefsTo(struct))) == 0: | ||
continue | ||
refTo = list(DataRefsTo(struct))[0] | ||
info.superFuncName = GetFunctionName(refTo) | ||
info.superFunc = LocByName(info.superFuncName) | ||
|
||
AllGlobalBlockMap[func] = info | ||
|
||
def funcIsGlobalBlockFunc(block_func): | ||
return block_func in AllGlobalBlockMap | ||
|
||
|
||
def isPossibleStackBlockForFunc(block_func): | ||
# def superFuncForStackBlock(block_func): | ||
|
||
if not isInText(block_func): | ||
return False | ||
|
||
if GetFunctionAttr(block_func,FUNCATTR_START) != (block_func & ~ 1): | ||
return False | ||
|
||
#block addr cannot be called directly | ||
if len(list(CodeRefsTo(block_func, 0))) !=0 : | ||
# print '%x is not block because be call by %x' % (block_func ,list(CodeRefsTo(block_func, 0))[0]) | ||
return False | ||
|
||
# ref to block should be in text section | ||
refsTo = list(DataRefsTo(block_func)) | ||
for addr in refsTo: | ||
if not isInText(addr): | ||
# print '%x is not block because be ref from %x' % (block_func, addr) | ||
return False | ||
|
||
# block func should be ref in only 1 function | ||
superFuncs = [GetFunctionAttr(x,FUNCATTR_START) for x in refsTo] | ||
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 = list(DataRefsTo(block_func)) | ||
superFuncs = [GetFunctionAttr(x,FUNCATTR_START) for x in refsTo] | ||
superFuncs = list (set (superFuncs)) | ||
if len(superFuncs) != 1: | ||
return None | ||
super_func_addr = superFuncs[0] | ||
return super_func_addr | GetReg(super_func_addr, "T") # 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 | ||
funcName = GetFunctionName(block_func) | ||
|
||
if len(funcName) != 0 and funcName[0] in ('-', '+'): | ||
return funcName | ||
|
||
# maybe nested block | ||
superBlockFuncAddr = superFuncForBlockFunc(block_func) | ||
if superBlockFuncAddr == None: | ||
return ""; | ||
|
||
superBlockFuncAddr = superBlockFuncAddr | GetReg(superBlockFuncAddr, "T") # thumb | ||
superBlockName = findBlockName(superBlockFuncAddr) | ||
|
||
if len(superBlockName) == 0: | ||
return "" | ||
else: | ||
return superBlockName + "_block" | ||
|
||
|
||
|
||
#find all possible Stack Block | ||
allPossibleStackBlockFunc = [] | ||
allRefToBlock=[] | ||
if IS32BIT: | ||
allRefToBlock = list(DataRefsTo(LocByName("__NSConcreteStackBlock"))) | ||
else: | ||
allRefToBlock = list(DataRefsTo(LocByName("__NSConcreteStackBlock_ptr"))) | ||
allRefToBlock.sort() | ||
|
||
''' | ||
2 ref (@PAGE , @PAGEOFF) to __NSConcreteStackBlock_ptr , | ||
but once actual | ||
filter the list | ||
__text:0000000102D9979C ADRP X8, #__NSConcreteStackBlock_ptr@PAGE | ||
__text:0000000102D997A0 LDR X8, [X8,#__NSConcreteStackBlock_ptr@PAGEOFF] | ||
''' | ||
tmp_array = allRefToBlock[:1] | ||
for i in range(1, len(allRefToBlock)): | ||
if allRefToBlock[i] - allRefToBlock[i - 1] <= 8: | ||
pass | ||
else: | ||
tmp_array.append(allRefToBlock[i]) | ||
allRefToBlock = tmp_array | ||
|
||
allRefToBlock = filter(lambda x:isInText(x), allRefToBlock) | ||
|
||
for addr in allRefToBlock: | ||
LineNumAround = 30 #Around 30 arm instruction | ||
scan_addr_min= max (addr - LineNumAround * 4, GetFunctionAttr(addr,FUNCATTR_START)) | ||
scan_addr_max= min (addr + LineNumAround * 4, GetFunctionAttr(addr,FUNCATTR_END)) | ||
for scan_addr in range(scan_addr_min, scan_addr_max): | ||
allPossibleStackBlockFunc += list(DataRefsFrom(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 | ||
|
||
|
||
output_file = './block_symbol.json' | ||
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, "w") | ||
f.write(encodeJson) | ||
f.close() | ||
|
||
print 'restore block num %d ' % len(list_output) | ||
print 'origin block num: %d(GlobalBlock: %d, StackBlock: %d)' % (len(allRefToBlock) + len(AllGlobalBlockMap), len(AllGlobalBlockMap), len(allRefToBlock)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters