# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
# version 0.2, now with less broken and more useful!
# Aaron Zinman
# take any .h from class_dump
# and this will generate a .m that overlaods the function with each method, logging input/output
# you get something like this
# 2007-09-24 02:44:44.183 MobilePhone[339:d03] PRE: (id) [Evil localizedLabel]
# 2007-09-24 02:44:44.187 MobilePhone[339:d03] PRE: (int) [Evil _addressBookUid]
# 2007-09-24 02:44:44.190 MobilePhone[339:d03] POST: returning -1
# 2007-09-24 02:44:44.208 MobilePhone[339:d03] POST: returning (null)
# 2007-09-24 02:44:44.323 MobilePhone[339:d03] PRE: (BOOL) [Evil isVoicemail]
# 2007-09-24 02:44:44.326 MobilePhone[339:d03] POST: returning b
# 2007-09-24 02:44:44.349 MobilePhone[339:d03] PRE: (id) [Evil displayName]
# 2007-09-24 02:44:44.351 MobilePhone[339:d03] PRE: (id) [Evil _displayNameWithSeparator: ]
# 2007-09-24 02:44:44.353 MobilePhone[339:d03] PRE: (int) [Evil _addressBookUid]
# 2007-09-24 02:44:44.358 MobilePhone[339:d03] POST: returning -1
# 2007-09-24 02:44:44.361 MobilePhone[339:d03] POST: returning Voicemail
# WARNING: will overwrite any evil* file that exists... Catagory based .h , i.e. PhoneApplication-Testing, will
# not be smart enough to append
# How to use:
# class-dump -aAHIsS MobilePhone
# python make_evil PhoneCall.h
# arm-apple-darwin-gcc -g -W -Wall -D_REENTRANT -fno-common -c -o EvilPhoneCall.o EvilPhoneCall.m
# arm-apple-darwin-gcc -undefined define_a_way -dynamiclib -lobjc -framework CoreFoundation -framework Foundation -framework UIKit -o evilPhoneCall.dylib EvilPhoneCall.o
# to load dylib overriding MobilePhone:
# $ scp evilPhoneCall.dylib root@iphone:/root@192.168.1.10
# $ ssh root@iphone
# > $ launchctl remove com.apple.springboard
#
# > $ DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=/evilPhoneCall.dylib /System/Library/CoreServices/SpringBoard.app/SpringBoard
# congrats, you are now evil!
import sys, re, os
if len(sys.argv) != 2 or sys.argv[1][-1] != 'h':
print "usage: %s classDump.h"
sys.exit(1)
# specs at http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFStrings/
# please correct my errors
def writeProperVarsOut(passedSet, passedTypes):
for var in passedSet:
if passedTypes[var] == 'struct CGSize':
evil.write(', %s.width, %s.height' % (var, var) )
elif passedTypes[var] == 'struct CGPoint':
evil.write(', %s.x, %s.y' % (var, var) )
elif passedTypes[var] == 'struct CGRect':
evil.write(', %s.size.width, %s.size.height, %s.origin.x, %s.origin.y' % (var, var, var, var) )
else:
evil.write(', ' + var)
def writeProperFormatter(messageParamType):
if messageParamType == 'id' or messageParamType.startswith('NS') or messageParamType.startswith('UI'):
evil.write('%@')
elif messageParamType == 'struct __CFArray *':
evil.write('%@')
elif messageParamType == 'struct CGPoint':
evil.write('(x=%1.3f, y=%1.3f)')
elif messageParamType == 'struct CGSize':
evil.write('(w=%1.3f, h=%1.3f)')
elif messageParamType == 'struct CGRect':
evil.write('(CGRect: w=%1.3f, h=%1.3f @ x=%1.3f, y=%1.3f)')
elif messageParamType == 'BOOL':
evil.write('%d')
elif messageParamType == 'unsigned char':
evil.write('%c')
elif messageParamType == 'char *':
evil.write('%s')
elif messageParamType == 'int' or messageParamType == 'long':
evil.write('%d')
elif messageParamType == 'int' or messageParamType == 'long':
evil.write('%d')
elif messageParamType == 'long long':
evil.write('%qi')
elif messageParamType == 'unsigned long long':
evil.write('%qu')
elif messageParamType == 'unsigned long':
evil.write('0x%x')
elif messageParamType == 'short':
evil.write('%hi')
elif messageParamType == 'unsigned short':
evil.write('%hu')
elif messageParamType == 'unichar':
evil.write('%C')
elif messageParamType == 'unichar *':
evil.write('%S')
elif messageParamType == 'void *':
evil.write('%p')
elif messageParamType == 'float' or messageParamType == 'double':
evil.write('%f')
else:
evil.write('(' + messageParamType + ')@0x%x')
f = open(sys.argv[1], 'r')
className = None
evilTestFileName = None
evil = None
for line in f:
if line.startswith("@interface"):
tok = line.split(' ')
className = tok[1]
evilTestFileName = 'Evil' + className + ".m"
evilClassName = 'Evil' + className
evil = open(evilTestFileName, 'w')
evil.write('#import "' + sys.argv[1] + '"\n\n')
evil.write('static int __evilTabCount = 0;\n\n')
evil.write('@interface ' + evilClassName + ' : ' + className + '\n{ }\n@end\n\n')
evil.write('@implementation ' + evilClassName + ' : ' + className + '\n')
evil.write('+ (void)load\n')
evil.write('{\n')
# evil.write('\tprintf("\\n**** %s: ' + evilClassName + ', loading!!\\n", getPidName());\n')
evil.write('\tprintf("\\n****' + evilClassName + ', loading!!\\n\\n");\n')
evil.write('\t[self poseAsClass: [%s class]];\n' % (className))
evil.write('}\n\n')
elif line.startswith("@end"):
evil.write("\n@end\n\n")
evil.flush()
evil.close()
className = None
evilTestFileName = None
evil = None
elif line.startswith("- ") or line.startswith("+ "):
passedSet = []
passedTypes = {}
precol = line[:line.find(';')]
func_name = precol[(precol.find(')')+1):]
evil.write(precol + "\n{\n")
# decompose
pat = re.compile("""[+-] \((.*?)\)""")
return_type = pat.findall(precol)[0]
args = precol[precol.find(return_type)+len(return_type)+1:]
# get args
pat = re.compile('\\s?(.+?):\((.+?)\)([a-zA-Z0-9_]+)')
args = pat.findall(args)
evil.write('\tNSString *tab = @""; ')
evil.write('\tint i = 0; for (; i < __evilTabCount; i++ ) ')
evil.write('tab = [tab stringByAppendingString:@"\\t"];\n')
#### NSLog(@"PRE: (void) [Evil %@ unknownPersonCardViewer: %@ dismissPickingOverlay: %@]\n", self, fp8, fp12);
passedSet.append('tab')
passedTypes['tab'] = 'id'
if not func_name.startswith('init'):
evil.write('\tNSLog(@"%@PRE: (' + return_type + ') [Evil %@ ')
passedSet.append('self')
passedTypes['self'] = 'id'
else:
evil.write('\tNSLog(@"%@PRE: [' + evilClassName + ' ')
if ( len(args) == 0 ):
#no args
evil.write(func_name)
else:
for argset in args:
messageName = argset[0]
messageParamType = argset[1]
messageParamName = argset[2]
passedSet.append(messageParamName)
passedTypes[messageParamName] = messageParamType
evil.write(' ' + messageName + ': ')
writeProperFormatter(messageParamType)
evil.write(']\\n"')
writeProperVarsOut(passedSet, passedTypes)
evil.write('); __evilTabCount++;\n')
#### [super unknownPersonCardViewer: fp8 dismissPickingOverlay: fp12 ];
evil.write('\t')
if return_type != 'void':
evil.write(return_type + ' supersays = ')
evil.write('[super ')
if ( len(args) == 0 ):
#no args
evil.write(func_name)
else:
for argset in args:
messageName = argset[0]
messageParamType = argset[1]
messageParamName = argset[2]
evil.write(messageName + ': ' + messageParamName + ' ')
evil.write('];\n')
#### NSLog(@"POST: returning %@', supersays)
if return_type != 'void':
evil.write('\tNSLog(@"%@POST: returning ')
writeProperFormatter(return_type)
evil.write('", tab')
writeProperVarsOut(['supersays'], {'supersays': return_type})
evil.write('); __evilTabCount--;\n\treturn supersays;\n')
else:
evil.write('\tNSLog(@"%@POST: returning void", tab);\n')
evil.write('\t__evilTabCount--;\n')
#### }
evil.write('}\n\n')