Neo
Neo

Reputation: 523

NDK need gnustl_static std::string instead of std::_ndk1::string

I am hooking an existing library that is compiled using gnustl std::string instead of libc++ std::_ndk1::string. If I try to set or access these strings I just get garbage. How do I convert my std::string to std::_ndk1::string and vice versa in my hook application?

I cannot compile my hook with "-DANDROID_STL=gnustl_shared" because it no longer exists and other libraries in use require libc++.

The documentation mentions this https://developer.android.com/ndk/guides/cpp-support "The various STLs are not compatible with one another. As an example, the layout of std::string in libc++ is not the same as it is in gnustl" which is exactly the problem.

Upvotes: 4

Views: 1173

Answers (2)

Neo
Neo

Reputation: 523

Here's what I came up with (for now, really not ideal):

StringsProxy.cc

#include "StringsProxy.h"
#include <iostream>
#include <string>

using namespace std;

__attribute__((visibility("default")))
extern "C" StringsProxy::StringsProxy(const char* contents)
{
    set_string = std::string(contents);
}
__attribute__((visibility("default")))
extern "C" StringsProxy::StringsProxy(uintptr_t str) {
    set_string = *reinterpret_cast<proxy_string*>(str);
}
__attribute__((visibility("default")))
extern "C" const char* StringsProxy::c_str() {
    return set_string.c_str();
}
__attribute__((visibility("default")))
extern "C" const uintptr_t* StringsProxy::ptr() {
    return reinterpret_cast<uintptr_t *>(&set_string);
}
__attribute__((visibility("default")))
extern "C" StringsProxy::~StringsProxy() {
}

StringsProxy.h

#ifndef __STRINGSPROXY_H__
#define __STRINGSPROXY_H__
#include <string>

typedef std::basic_string<char> proxy_string;

class StringsProxy
{
public:
  /* Initialize StringsProxy with a pointer to an existing string */
  StringsProxy(uintptr_t str);
  /* Initialize StringsProxy with a new string */
  StringsProxy(const char* str);
  /* Get C string */
  virtual const char* c_str();
  /* Get pointer to string for injection */
  const virtual uintptr_t* ptr();

private:
  proxy_string set_string;
};
#endif

Compile this into a shared object using the old NDK with -DCMAKE_ANDROID_STL_TYPE=gnustl_static

Then link this shared object to the hooking program (in CMakeLists): target_link_libraries(${TARGET_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/abiproxy/build/armeabi-v7a/libabiproxy.so)

Then in the hooking program, can be used like this:

#include "abiproxy/StringsProxy.h"
void *setUriDebug(uintptr_t a1, uintptr_t stri) {

    auto y = StringsProxy(stri);
    LOGI("URI CALLED %s", y.c_str());

    return setUriDebugOld(a1, stri);
}

Or in reverse:

StringsProxy assetNameBaseProxy = StringsProxy("https://example.com/");
void setResourceUrl(uintptr_t* a1, int a2) {    
    *(a1 + 1) = *assetNameBaseProxy.ptr();
}

This isn't by any means a good solution, but it works for my use-case.

Upvotes: 2

Alex Cohn
Alex Cohn

Reputation: 57183

To use gnustl, you could compile all your native code with NDK r.17 or older. This is a dangerous path, because many important bugs, including security-related, have been fixed since then.

Another unsupported (and not recommended) way to deal with your problem is to get the gnustl sources from NDK r.17 and compile them with the latest NDK version.

Your best option is to have all your dependencies rebuilt with a recent version of NDK and its c++_shared runtime library.

Upvotes: 4

Related Questions