Friday, April 16, 2021

Reading C++'s std::string with Frida

 Something that is very interesting to us is the ability to read strings, however this is not always possible by simply calling FRIDA’s readUtf8String/readCString built-ins due to the different ways a string can be represented. For example, Window’s UNICODE_STRING is defined in a struct as follows:

typedef struct _UNICODE_STRING {

  USHORT Length;

  USHORT MaximumLength;

  PWSTR  Buffer;

} UNICODE_STRING, *PUNICODE_STRING;

A common string type to parse is a C++ std::string. A similar concept will be seen in Swift.String’s datatype later on. For std::string’s the LSB (Least Significant Bit) will store 0 if the it is a short string (< 22 bytes) or 1 for long strings. If it is a long string, the pointer to the string we want to get will be stored at two times the Process.pointerSize of the process we are attached to. To test this knowledge out and see how to obtain the string, let’s see this simple program:

#include <iostream>

void print_std_string(std::string arg_1)

{

std::cout << arg_1 << std::endl;

}

int

main(void)

{

std::string my_string = "FRIDA is great, you should check it out at frida.re";

print_std_string(my_string);

return 0;

}

This program simply calls the print_std_string(std::string arg_1) function and prints it to screen. This way it is easy to get the std::string parameter and inspect it. Once we fire up this program in FRIDA’s REPL and run Module.enumerateExportsSync() on our binary we notice that names are mangled, but due to the name we have chosen for the test function we can spot a mangled function named _Z16print_std_stringNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE. This is the function we want to use Interceptor.attach on.

Interceptor.attach(Module.getExportByName(null, '_Z16print_std_stringNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE'), {

onEnter (args) {

const LSB = args[0].readU8() & 1;

console.log('LSB: ' + LSB);

const stdString = args[0].add(Process.pointerSize*2).readPointer().readUtf8String();

console.log("std::string: " + stdString);

}

});

Then, we can run this small script and get the following output:

LSB: 1

std::string: FRIDA is great, you should check it out at frida.re

[Local::a.out]-> Process terminated

It is important to address that this was tested using clang++ 12.0.0, the memory layout may differ within compilers such as GCC which implements unions to store small strings

No comments:

Post a Comment

2023

Every year I start writing about a wrap-up of my year but I never end up finishing it. Hope this year is different. I'm starting with th...