Viewing strings
There are two ways to view the representation of an IL2CPP string in Xcode. We can view the memory of a string directly,
or we can call one of the string utilities in libil2cpp to convert the string to a std::string, which Xcode can display.
Let’s look at the value of the string named _stringLiteral1 (spoiler alert: its contents are "Hello, IL2CPP!").
In the generated code with
Ctags
built (or using Cmd+Ctrl+J in Xcode), we can jump to the definition of _stringLiteral1 and
see that its type is Il2CppString_14:
struct Il2CppString_14
{
Il2CppDataSegmentString header;
int32_t length;
uint16_t chars[15];
};
In fact, all strings in IL2CPP are represented like this. You can find the definition of Il2CppString in the object-internals.h
header file. These strings include the standard header part of any managed type in IL2CPP, Il2CppObject
(which is accessed via the Il2CppDataSegmentString typedef), followed by a four byte length, then an array of two bytes characters.
Strings defined at compile time, like _stringLiteral1 end up with a fixed-length chars array, whereas strings created at
runtime have an allocated array. The characters in the string are encoded as UTF-16.
If we add _stringLiteral1 to the watch window in Xcode, we can select the View Memory of “_stringLiteral1” option to see the
layout of the string in memory.
Then in the memory viewer, we can see this:
The header member of the string is 16 bytes, so after we skip past that, we can see that the four bytes for the
size have a value of 0x000E (14). The next byte after the length is the first character of the string, 0x0048 (‘H’).
Since each character is two bytes wide, but in this string all of the characters fit in only one byte, Xcode displays them
on the right with dots in between each character. Still, the content of the string is clearly visible. This method of viewing
string does work, but it is a bit difficult for more complex strings.
We can also view the content of a string from the lldb prompt in Xcode. The utils/StringUtils.h header gives us the
interface for some string utilities in libil2cpp that we can use. Specifically, let’s call the Utf16ToUtf8 method from the
lldb prompt. Its interface looks like this:
static std::string Utf16ToUtf8 (const uint16_t* utf16String);
We can pass the chars member of the C++ structure to this method, and it will return a UTF-8 encoded std::string.
Then, at the lldb prompt, if we use the p command, we can print the content of the string.
(lldb) p il2cpp::utils::StringUtils::Utf16ToUtf8(_stringLiteral1.chars)
(std::__1::string) $1 = "Hello, IL2CPP!"