Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting argument type in a method

I am trying to subclass a certain class and override some of its private methods. This is dangerous, could fail, could not pass Apple's review (In the AppStore), could have terrible effects, etc, but this is just for experimental/educational purposes :)

For example let's say I want to know the type of first argument of the instance method keyboardInput:shouldInsertText:isMarkedText: of UITextView :

SEL selector = @selector(keyboardInput:shouldInsertText:isMarkedText:);
Class class = [UITextView class];
Method method = class_getInstanceMethod(class, selector);
char *arg = method_copyArgumentType(method, 0);
printf("_%s_\n", arg);
free(arg);

However In the console I only get _@_ . I think @ means object but an object of what class? (I thought I would get the name of the class of the argument) Is it possible to get the class of that parameter using other objective-c runtime functions or any other mean?

PS: In the example I used a class of Cocoa touch but I could have tried exactly the same with a class of Cocoa. So this should not be specific to iOS.

SOLVED:

I have tried in the simulator what Dave suggested, it worked!

(gdb) info all-registers gave me a long list of values,...

((gdb) po *(id*)($ebp + 8)
<MyTextView: 0x5911270; baseClass = UITextView; frame = (80 70; 240 323); text = 'Lorem ipsum dolor sit er ...'; clipsToBounds = YES; autoresize = RM+BM; layer = <CALayer: 0x5c0c7d0>; contentOffset: {0, 0}>

(gdb) p *(SEL*)($ebp + 12)
$5 = (SEL) 0xbd19

(gdb) po *(id*)($ebp + 16)
<UIWebDocumentView: 0xa02f000; frame = (0 0; 240 457); text = 'Lorem ipsum dolor sit er ...'; opaque = NO; userInteractionEnabled = NO; layer = <UIWebLayer: 0x5c35070>>

(gdb) po *(id*)($ebp + 20)
t

(gdb) p *(id*)($ebp + 24)
$6 = (id) 0x0

Which makes sense since the key I pressed was a single "t" so it was not marked text ("0x0") so I is OK to think that the first argument should be of type UIWebDocumentView.

Just one little thing (irrelevant in this case), how could I get the method name from a SEL in gbd? for example $5 ?

like image 899
nacho4d Avatar asked Jul 16 '11 02:07

nacho4d


2 Answers

You cannot tell the class of the argument from introspection of the interface. All id are created equal in Objective-C, once you've been compiled. You can, however, ask a specific instance its -class. So if you're trying to reverse engineer an internal method, you'd need to swizzle the method, get it to be called, and then introspect the objects you're passed. Google "method swizzling" for plenty of discussion of how to inject your code into any Objective-C method.

like image 140
Rob Napier Avatar answered Sep 27 '22 23:09

Rob Napier


Assuming this is for academic interests only....

One way you could find out is to break on the symbol in GDB and inspect the parameters. You know what general kind they are (ie, float vs object, etc), so you can simply use the right p command to print them.

To set the breakpoint, you can either do:

  1. In the gdb console, type b -[UITextView keyboardInput:shouldInsertText:isMarkedText:]
  2. In Xcode, add a new symbolic breakpoint for -[UITextView keyboardInput:shouldInsertText:isMarkedText:]

When you actually hit the breakpoint, you're not going to see anything useful (of course). But you can print the arguments out at this point, because you know their sizes, and thus where they should be in the registers.

For more info on which registers they're in, check out this handy blog post. You can also do something like info all-registers to print out all the information in all the registers, etc.


update

If you want to print the name of the selector, you can use a little trick, which is that selectors are more-or-less* char*s:

p (char*)$5

Will print:

${some number} = 0x{some address} "keyboardInput:shouldInsertText:isMarkedText:"
like image 28
Dave DeLong Avatar answered Sep 27 '22 23:09

Dave DeLong