As the most important parts of Adtrace SDK implementation for applications, appToken
and SDK signatures
must be carefully taken care of. it's not that hard to extract any key from an APK using reverse engineering. however accessing Adtrace apptoken
or SDK signatures
does not have anything to do with hacking our SDK, because we use robust algorithms to validate every request and guarantee that it is coming from your application and not anywhere else.
- Introduction
- Download the NDK and build tools
- Create new C/C++ source file
- Configure CMake
- Add NDK APIs
- Simple C++ code using JNI
One of the safest ways to hide this sensitive data is Using NDK to add C and C++ code to your android project. all secret information will be stored in C++ file and backed inside .so
file. it's extremely hard to extract string from a .so
file because C file is converted to machine code. if anyone anyhow is able to extract the information from .so
file the secret text will be in hex value which is not readable and SDK signatures
are even harder to be found because of their numeral characteristics.
NDK is a toolset that allows you to use C and C++ code with Android, and provides platform libraries that allow you to manage native activities and access physical device components, such as sensors and touch input.
an external build tool that works alongside Gradle to build your native library. You do not need this component if you only plan to use ndk-build.
the debugger Android Studio uses to debug native code. you can read more details about how to install and config in here.
To add new C/C++
source files to an existing project, proceed as follows:
- If you don't already have a
cpp/
directory in the main source set of your app, create one as follows: a. Open the Project pane from the left side of the IDE and select the Project view from the drop-down menu. b. Navigate to your-module > src, right-click on the main directory, and select New > Directory. c. Entercpp
as the directory name and click OK. - Right-click on the
cpp/
directory and select New > C/C++ Source File. - Enter a name for your source file, such as
native-lib
. - From the Type drop-down menu, select the file extension for your source file, such as
.cpp
.
- You can add other file types to the drop-down menu, such as
.cxx
or.hxx
, by clicking Edit File Types . In the C/C++ dialog box that pops up, select another file extension from the Source Extension and Header Extension drop-down menus and click OK.
- If you also want to create a header file, check the Create an associated header checkbox.
- Click OK.
A CMake build script is a plain text file that you must name CMakeLists.txt
and includes commands CMake uses to build your C/C++ libraries. If your native sources don't already have a CMake build script, you need to create one yourself and include the appropriate CMake commands.
# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.
cmake_minimum_required(VERSION 3.4.1)
# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add_library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.
add_library( # Specifies the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
When you add a source file or library to your CMake build script using add_library()
, Android Studio also shows associated header files in the Project view after you sync your project. However, in order for CMake to locate your header files during compile time, you need to add the include_directories()
command to your CMake build script and specify the path to your headers:
add_library(...)
# Specifies a path to native header files.
include_directories(src/main/cpp/include/)
The convention CMake uses to name the file of your library is as follows:
liblibrary-name.os
For example, if you specify "native-lib"
as the name of your shared library in the build script, CMake creates a file named libnative-lib.so
. However, when loading this library in your Java or Kotlin code, use the name you specified in the CMake build script:
static {
System.loadLibrary("native-lib");
}
companion object {
init {
System.loadLibrary("native-lib");
}
}
Note: If you rename or remove a library in your CMake build script, you need to clean your project before Gradle applies the changes or removes the older version of the library from your APK. To clean your project, select Build > Clean Project from the menu bar.
Android Studio automatically adds the source files and headers to the cpp group in the Project pane. By using multiple add_library()
commands, you can define additional libraries for CMake to build from other source files.
Add the find_library()
command to your CMake build script to locate an NDK library and store its path as a variable. You use this variable to refer to the NDK library in other parts of the build script. The following sample locates the Android-specific log support library and stores its path in log-lib
:
find_library( # Defines the name of the path variable that stores the
# location of the NDK library.
log-lib
# Specifies the name of the NDK library that
# CMake needs to locate.
log )
In order for your native library to call functions in the log
library, you need to link the libraries using the target_link_libraries()
command in your CMake build script:
find_library(...)
# Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the log library to the target library.
${log-lib} )
in order to receive information from native code you have to include
JNI inside C++ file(native-lib
).
#include <jni.h>
since appToken
is String you need to add string
class too.
#include <string>
now appToken
value can be hard coded and passed to classes that loadLibrary
method is implemented in. this will allow you to load the library you created in C++ and of course the information you need to obtain from it.
an example of returning appToken
from native-lib.cpp
to MainActivity.java
:
JNIEXPORT jstring JNICALL
Java_com_example_nativecpplib_MainActivity_getAppToken(
JNIEnv *env,
jobject thiz) {
std::string token = "x1y2z3abc123";
return env->NewStringUTF(token.c_str());
}
by which method name mean:
part | reference |
---|---|
Java |
Language |
com_example_nativecpplib |
package name |
MainActivity |
class name |
getAppToken |
native method used in class |
and Java
method would obtain value to be used in any parts of code. for appToken
it is like:
public native String getAppToken();
//method body does not exists in java code in MainActivity
/*
...
*/
String appToken = getAppToken();
String environment = AdTraceConfig.ENVIRONMENT_SANDBOX;
AdTraceConfig config = new AdTraceConfig(this, appToken, environment);
AdTrace.onCreate(config);
you can repeat this process for SDK signatures
too afterwards:
JNIEXPORT jlongArray JNICALL
Java_com_example_nativecpplib_MainActivity_getSignatures(
JNIEnv *env,
jobject thiz) {
jlong sign1 = 123456789;
jlong sign2 = 987654321;
jlong sign3 = 123456789;
jlong sign4 = 987654321;
jlong id = 1;
jlongArray res ;
res=env -> NewLongArray(5);
jlong fill[5];
fill[0] = id;
fill[1] = sign1;
fill[2] = sign2;
fill[3] = sign3;
fill[4] = sign4;
(*env).SetLongArrayRegion(res,0,5,fill);
return res;
}
and:
public native long[] getSignatures();
//method body does not exists in java code in MainActivity
/*
...
*/
AdTraceConfig config = new AdTraceConfig(this, appToken, environment);
long[] sdkSignatures = getSignatures();
sdkSignatures[1];
config.setAppSecret(
sdkSignatures[0], //secretId
sdkSignatures[1], //info1
sdkSignatures[2], //info2
sdkSignatures[3], //info3
sdkSignatures[4] //info4
);
AdTrace.onCreate(config);