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

如何使用本机代码以编程方式查找Java局部变量的内存地址?,java,android,c++,c,android-ndk,Java,Android,C++,C,Android Ndk,虽然也有类似的问题(例如,),但他们的答案并不能解决我的问题 我使用的是Android NDK和Android Studio 1.5.1,目标是Android API 18(在Android KitKat 4.4之前,所以我使用的是Dalvik,而不是ART runtime) 我知道Dalvik解释器堆栈上应该有一个原始Java局部变量,但我找不到它 我使用以下代码在Java代码中声明Java本地整数幻数(0x23420023),并使用本机代码(C代码)搜索它 我将Java代码的进程id(pid

虽然也有类似的问题(例如,),但他们的答案并不能解决我的问题

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

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

我使用以下代码在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-缓存/data@app@com.example.magicnumber2-1。apk@classes.dex

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

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

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);
}
}
#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));
        }
    }
}
#包括“com_example_magicnumber2_main activity.h”
#包括
#包括
#包括
#包括
JNIEXPORT jboolean JNICALL Java_com_示例_MagicNumber 2_Main Activity_findMagicNumber(JNIEnv*env、jobject obj、jint pid、jint tid){
long startaddr、endaddr、大小、偏移量、索引节点;
字符权限[8],设备[8],文件名[200],行[250];
字符*开始,*结束,*候选字符;
char文件路径[100];
//pid是进程id,tid是Java代码中Java调用方法的线程id
sprintf(文件路径,“/proc/%d/task/%d/maps”,pid,tid);
FILE*FILE=fopen(文件路径,“r”);
while(fgets(行、sizeof(行)、文件)){
memset(文件名'\0',sizeof(文件名));
sscanf(行“%llx-%llx%s%llx%s%llx%s”、&startaddr、&endaddr、权限、偏移量、设备和索引节点、文件名);
//1-dex:仅检查映射到app-dex文件的内存区域
如果((strstrstr(filename,.dex))==NULL)继续;
//2-权限:仅检查读、写和专用内存区域
//如果((strstrstr(permissions,“rw-p”))==NULL)继续;
//3-inode:仅检查未映射到文件或设备的内存区域
//如果(inode!=0)继续;
__android日志打印(android日志调试,“:”,%llx-%llx%s%llx%s%llx%s”,
startaddr、endaddr、权限、偏移量、设备、索引节点、文件名);
start=STARTADR;
end=endaddr;
候选者=memchr(开始,0x14,(结束-开始));
while(候选者!=0){
if((候选者[2]==0x23)&&
(候选[3]==0x00)&&
(候选[4]==0x42)&&
(候选[5]==0x23)){
__android_log_print(android_log_DEBUG,@@@@@@@@@@,神奇数字位于%p),候选);
打破
}
其他的
候选者=memchr(候选者+1,0x14,(最终候选者));
}
}
}

JNI提供了一种访问本机代码中java变量的机制,如下所述:

然后你可以用

  &x; // gets the address of x
获取变量的地址

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

#包括
未签名的长get_sp(){
__asm_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;
}
无符号长get_bp(){
__asm_uu u__;(“mov%rbp,%rax”);
}
int main(int argc,字符**argv)
{
int n;
printf(“SP 0x%x\n”,get_SP());
printf(“BP 0x%x\n”,get_BP());
返回0;
}

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