如何使用本机代码以编程方式查找Java本地变量的内存地址



尽管有类似的问题(如1、2、3),但它们的答案并不能解决我的问题。

我使用Android NDK和Android Studio 1.5.1,目标是Android API 18(在Android KitKat 4.4之前,所以我处理的是Dalvik,而不是ART运行时)。

我知道一个原始Java本地变量应该在Dalvik解释器堆栈上,但我找不到它

我使用以下代码在Java代码中声明了一个Java本地整数幻数(0x23420023),并使用本地代码(C代码)进行搜索。

我将Java代码的进程id(pid)和线程id(tid)传递给C代码,这样我就可以搜索声明该幻数变量的Java方法所占用的虚拟地址空间。

在C代码中,我通过读取和解析文件/proc/pid/task/tid/maps 来获得Java代码的内存区域

问题出在哪里

当我扫描内存区域(从文件/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

我可以立即找到神奇的数字,但问题是内存区域被应用程序对象文件占用,而不是Dalvik堆栈。您可以通过取消注释标记为"//1-dex:"的第一个if语句,并注释掉C代码中两个while循环之间标记为"//2-permission:"one_answers"//3-inode"的第二个和第三个if语句来确认这一点。

然而,当我搜索其他具有读、写和私有权限"rw-p"(因为Dalvik堆栈应该具有读/写/私有权限)的剩余内存区域(从文件/proc/pid/task/tid/maps中提取)时,我得到了一个分段错误。您可以通过注释掉C代码中两个while循环之间标记为"//1-dex:"的第一个if语句,并取消注释标记为"//2-permission:"one_answers"//3-inode"的第二个和第三个if语句来确认这一点。

Java代码:

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);
}
}

C代码:

#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, '', 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));
        }
    }
}

JNI提供了一种访问本地代码中java变量的机制,如下所述:http://www.math.uni-hamburg.de/doc/java/tutorial/native1.1/implementing/field.html

然后你可以使用

  &x; // gets the address of x

获取变量的地址

另一种方法是使用汇编程序,即以下代码打印堆栈的开头(通过打印堆栈指针和基指针的地址)

#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%xn", get_sp());
printf("BP 0x%xn", get_bp());
return 0;
}

最新更新