【问题标题】:How to find the memory address of a Java local variable programmatically using a native code?如何使用本机代码以编程方式查找 Java 局部变量的内存地址?
【发布时间】:2016-01-06 05:55:31
【问题描述】:

虽然有类似的问题(如123),但他们的回答并没有解决我的问题。

我正在使用面向 Android API 18 的 Android Studio 1.5.1 的 Android NDK(在 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 语句并注释掉标记为“//2-permission:”和“//3-inode”之间的第二个和第三个 if 语句来确认这一点C 代码中的两个 while 循环。

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

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

【问题讨论】:

  • 在虚拟机中,无法保证局部变量可能在哪里(如果有的话)。笔记;即使您找到冷代码的位置,如果代码升温,它也可能会改变(尽管 Android 不倾向于这样做)
  • int magicNumber = 0x23420023 ; 不需要在任何地方分配,编译器可以(并且很可能会)将其作为常量省略

标签: java android c++ c android-ndk


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-08
    • 1970-01-01
    • 1970-01-01
    • 2011-12-20
    • 2014-09-08
    • 2012-06-05
    • 2023-04-04
    相关资源
    最近更新 更多