【发布时间】:2016-01-04 17:35:08
【问题描述】:
我将 JNI 与面向 Android API 18 的 Android Studio 1.5.1 一起使用,我的问题是:
Q) 在不使用工具或更改/修改 Dalvik VM 源代码的情况下,如何从本地代码中找到 Dalvik Stack 上 Java 局部变量的内存地址?
例如,我尝试使用以下代码(改编自互联网)查找 Java 局部变量 magicNumber = 0x23420023 的内存地址,但出现分段错误错误。
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);
}
}
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <stdlib.h>
#include "com_example_magicnumber2_MainActivity.h"
#include <unistd.h>
#include <memory.h>
#define ENOENT 2 /* No such file or directory */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define EINVAL 22 /* Invalid argument */
jboolean validAddress(char* address)
{
if ((access(address, F_OK)==-1) && !(errno == ENOENT) && !(errno == ENAMETOOLONG))
return JNI_FALSE;
else if ((access(address, F_OK)==-1) && (errno == ENOMEM) ||
(access(address, F_OK)==-1) && (errno == EACCES) ||
(access(address, F_OK)==-1) && (errno == EFAULT) ||
(access(address, F_OK)==-1) && (errno == EINVAL))
return JNI_FALSE;
else if (address == NULL)
return JNI_FALSE;
else
return JNI_TRUE;
}
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;
int result, i = 0;
char filepath[100];
sprintf(filepath,"/proc/%d/task/%d", pid, tid);
FILE* file = fopen(filepath, "r");
jboolean found = JNI_FALSE;
while (fgets(line, sizeof(line), file) && !found) {
sscanf(line,"%llx-%llx %s %llx %s %llx", &startaddr, &endaddr, permissions, &offset, device, &inode);
start = startaddr;
end = endaddr;
mprotect( (void*)start , (end-start), PROT_READ);
candidate = memchr( start, 0x14, (end-start));
while( validAddress(candidate) && !found){
if ((validAddress(candidate[2]) && (candidate[2]== 0x23)) &&
(validAddress(candidate[3]) && (candidate[3] == 0x00)) &&
(validAddress(candidate[4]) && (candidate[4] == 0x42)) &&
(validAddress(candidate[5]) && (candidate[5] == 0x23))){
__android_log_print(ANDROID_LOG_DEBUG,"***","Location=%p WE FOUND IT!", candidate);
found = JNI_TRUE;
break;
return JNI_TRUE;
}
else if ((validAddress(candidate)) &&
validAddress(candidate=memchr(candidate+1, 0x14, (end-candidate)))){;
}
}
}
}
这是一个更新:
我之前贴的代码不是最新的,这里是最新的:
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 );
}
}
本机代码:
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;
int result, i = 0;
char filepath[100];
sprintf(filepath,"/proc/%d/task/%d/maps", pid, tid);
FILE* file = fopen(filepath, "r");
jboolean found = JNI_FALSE;
while (fgets(line, sizeof(line), file) && !found) {
sscanf(line,"%llx-%llx %s %llx %s %llx %s", &startaddr, &endaddr, permissions, &offset, device, &inode, filename);
if (((strstr(filename, "apk@classes.dex")))==NULL) {
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 && !found){
if ((candidate[2]== 0x23) &&
(candidate[3] == 0x00) &&
(candidate[4] == 0x42) &&
(candidate[5] == 0x23)){
__android_log_print(ANDROID_LOG_DEBUG,"********************************************************************","WE FOUND IT at %p!!!", candidate);
found = JNI_TRUE;
break;
}
else
candidate=memchr(candidate+1, 0x14, (end-candidate));
}
}
}
此代码正在运行,它可以找到幻数,但它在映射到 /data/dalvik-cache/data@app@com.example.magicnumber2-1.apk@classes.dex 的内存区域中找到它不是 Dalvik 堆栈。
但是,通过运行上面的代码并查看这两篇论文:paper1(附录B,只有寻蛋代码,我不需要更改任何Dalvik代码,因此跳过代码更改部分)和paper2,我们可以注意到以下内容(下面还有关于fadden的评论):
(1) 似乎int值magicNumber存储在一个Dalvik寄存器中。此外,它似乎存储在 Dalvik 堆栈中,而不是在本机代码堆栈中,因为 int 变量 magicNumber 已在 Java 代码部分中声明并赋值。
(2) 根据论文 1,answer 和运行附加的最新代码作为证据,我们不是使用 memchr 函数搜索 0x14,但我们要确保我们位于存储单元的开头将 int 存储在 ARM CPU 中。
(3) 我不需要再次调用 findMagicNumber 函数。我只需要在Dalvik堆栈中找到幻数的内存地址
(4) 我不需要找到 MagicNumber 附近的变量,所以这对我来说不是问题。
(5) 该项目只针对 Dalvik,所以 ART 不是问题
(6) 我同意,使用 mprotect() 不是一个好主意,在我的情况下也没有必要。
(7) 如果你参考paper2,你可以看到access() 可以用于它不是为它设计的东西,检查虚拟内存地址是否有效。我没有将 access() 用于任何与文件相关的操作,尽管它是为此目的而编写的
(8) 我不需要更改变量。我只需要以编程方式获取 Dalvik 堆栈上变量 magicNumber 的地址,无需使用任何工具或更改 Dalvik 源代码
我想知道 /proc/pid/task/tid/maps Dalvik 使用哪些内存区域来存储它的堆栈。
正如您在论文 1 中看到的,作者在 B.2 节第 4 行中没有解释他们使用 procs/maps 中的哪个内存区域来为 start 和 end 变量分配它们的值。
【问题讨论】:
标签: android java-native-interface stack native dalvik