简体   繁体   中英

How to find the memory address of a Java local variable programmatically using a native code?

Although there are similar questions (such as 1 , 2 , 3 ), their answers do not solve my problem.

I'm using Android NDK with Android Studio 1.5.1 targeting Android API 18 (before Android KitKat 4.4, so I'm dealing with Dalvik, not ART runtime).

I know that a primitive Java local variable should be on the Dalvik interpreter stack but I could not find it.

I used the following code to declare a Java local integer magic number (0x23420023) in the Java code and search for it using a native code (C code).

I pass the process id (pid) and the thread id (tid) of the Java code to the C code, so I can search the virtual address space occupied by Java method that declared that magic number variable.

In the C code, I get the memory regions for the Java code by reading and parsing the file /proc/pid/task/tid/maps

What is the problem?

When I scan the memory region (extracted from the file /proc/pid/task/tid/maps):

ad5b1000-ad7d7000 r--p 00000000 1f:01 756 /data/dalvik- cache/data@app@com.example.magicnumber2-1.apk@classes.dex

I can find the magic number immediately but the problem is that memory region is occupied by the app object file and not the Dalvik stack. You can confirm that by un-commenting the first if-statement labeled “//1-dex:” and commenting out the second and third if-statments labeled “//2-permission:” and “//3-inode” between the two while-loops in the C code.

However, when I search the other remaining memory regions (extracted from the file /proc/pid/task/tid/maps) that have read, write, and private permissions “rw-p” (because the Dalvik stack should have a read/write/private permissions), I get a segmentation fault error. You can confirm that by commenting out the first if-statement labeled “//1-dex:” and un-commenting the second and third if-statments labeled “//2-permission:” and “//3-inode” between the two while-loops in the C code.

The Java code:

public class MainActivity extends AppCompatActivity {
static {
    System.loadLibrary("MyLibrary");
}

public native boolean findMagicNumber(int pid, int tid);
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    int magicNumber = 0x23420023 ;
    int pid = android.os.Process.myPid();
    int tid = android.os.Process.myTid();
    findMagicNumber(pid, tid);
    System.out.println("********** magicNumber = " + magicNumber + " PID=" + pid + " TID=" + tid);
}
}

The C code:

#include "com_example_magicnumber2_MainActivity.h"
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <string.h>


JNIEXPORT jboolean JNICALL Java_com_example_magicnumber2_MainActivity_findMagicNumber(JNIEnv *env, jobject obj, jint pid, jint tid) {

    long long startaddr, endaddr, size, offset, inode;
    char permissions[8], device[8], filename[200], line[250];
    char *start, *end, *candidate;
    char filepath[100];

    //pid is the process id and tid is the thread id of the Java calling method from the Java code
    sprintf(filepath,"/proc/%d/task/%d/maps", pid, tid);
    FILE* file = fopen(filepath, "r");

    while (fgets(line, sizeof(line), file)) {
        memset( filename, '\0', sizeof(filename) );
        sscanf(line,"%llx-%llx %s %llx %s %llx %s", &startaddr, &endaddr, permissions, &offset, device, &inode, filename);

        //1-dex: examine only the memory region mapped to the app dex file
        if ((strstr(filename,".dex"))==NULL) continue;

        //2-permission: examine only read, write, and private memory regions
        //if (((strstr(permissions, "rw-p")))==NULL) continue;

        //3-inode: examine only the memory region that is not mapped to a file or device
        //if (inode !=0) continue;

        __android_log_print(ANDROID_LOG_DEBUG,":", "%llx-%llx %s %llx %s %llx %s",
                            startaddr, endaddr, permissions, offset, device, inode, filename);
        start = startaddr;
        end = endaddr;
        candidate = memchr( start, 0x14, (end-start));
        while( candidate !=0){
            if ((candidate[2]== 0x23) &&
                (candidate[3] == 0x00) &&
                (candidate[4] == 0x42) &&
                (candidate[5] == 0x23)){
                __android_log_print(ANDROID_LOG_DEBUG,"@@@@@@@@@@","The magic number is found at %p", candidate);
                break;
            }
            else
                candidate = memchr(candidate+1, 0x14, (end-candidate));
        }
    }
}

the JNI provides a mechanism for accessing the java variables in the native code, this is described here: http://www.math.uni-hamburg.de/doc/java/tutorial/native1.1/implementing/field.html

then you can use

  &x; // gets the address of x

to get the address of your variable

another approach is to use assembler, ie the following code prints the begin of the stack (by printing the address of stack pointer and base pointer)

#include <stdio.h>

unsigned long get_sp(){
__asm__("mov %rsp, %rax ");
}

unsigned long get_bp(){
__asm__("mov %rbp, %rax ");
}

int main(int argc, char **argv)
{
int n;
printf("SP 0x%x\n", get_sp());
printf("BP 0x%x\n", get_bp());
return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM