Dumping text from a NSString in memory

The solutions to the crackmes in this blog would not have been possible without finding a solution to a common problem: If I have a NSString pointer in a register (let’s assume $eax), is there a way to find the location in memory to the string characters in a certain encoding ? This was a problem because many times in the crackmes we have a trivial [isEqualToString:] operation going on, and we want to know what strings it is actually comparing. While digging around I found a solution, although it does not always work. Let’s take an example from the Fox crackme:

Example image

In this example, isEqualToString will compare the result of the previous method (uppercaseString) with the NSString we have inserted in the license. A good spot to intervene is 0x274d. Usually (and this is the case) the result of the call to the method is put in $eax. $eax now contains a pointer to an NSString object. If we try to display the contents of memory at that address, we will see apparently meaningless bytes of information. What I have tried is the following: I have created a simple Xcode project that creates a NSString literal and calls:

1
const char *temp = [string UTF8String];

At that point, I wrote down the address of temp, and I went back to the address of the NSString in memory (with the Xcode debugger). After some looking around, I succeeded in finding the address in temp starting from the 8th byte (in 32-bit) or from the 16th byte (in 64bit). Of course, the address is in little-endian format, so actually its byte order is reversed. Of course, the address is 4 bytes long in 32bit and 8 bytes long in 64bit applications. Continuing from the Fox crackme, this is the memory, in Hopper Disassembler, starting at the address in $eax :

Example image

Starting from the 8th byte, we have 0x208C3D00. If we display memory at that address reversed, that is 0x003D8C20, we find the string, apparently as UTF8 characters:

Example image

Voilà, we found the readable characters ! unfortunately this does not work at all cases but really helps. You can see, strangely enough, that the string is stored as a Pascal string, that is with the prefix value being the length of the string. In this case it is 0x28 = 40 characters. I have written a small c snippet that demonstrates the above theory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#ifdef __i386__
NSLog(@"Running in 32bit");
#define difference 8
#endif
#ifdef __x86_64__
NSLog(@"Running on 64 bit");
#define difference 16
#endif

int a;

for (a=0;a<100000;a++) {

NSString *string = @"A sample string";

NSLog(@"%@",string);

char *stringAsCharPointer = (char *)string;

stringAsCharPointer = stringAsCharPointer + difference;

long *address = (long *)stringAsCharPointer;

char *calculated = (char *)*address;

const char *fromCocoa = [string UTF8String];

if (fromCocoa != calculated) {
NSLog(@"They are different");
}

}

This is a test with 100.000 iterations and the calculated address is never different from the one that [UTF8String] returns.