我正在使用针对Android API 18的Android Studio 1.5.1的JNI,我的问题是:
问)在不使用工具或更改/修改Dalvik VM源代码的情况下,如何从本机代码中找到Dalvik Stack上Java局部变量的内存地址?
例如,我尝试使用以下代码(改编自Internet)来查找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,但我们想确保我们处于开头的在ARM CPU中存储int的内存单元。
(3)我不需要再次调用findMagicNumber函数。我只需要在Dalvik堆栈中找到幻数的内存地址
(4)我不需要在MagicNumber中找到附近的变量,所以在我的情况下这不是问题。
(5)该项目仅针对Dalvik,因此ART不是问题
(6)我同意,使用mprotect()不是一个好主意,在我的情况下不是必需的。
(7)如果您参考paper2,您可以看到access()可以用于非设计用途,检查虚拟内存地址是否有效。我没有将access()用于任何与文件相关的操作,尽管它是为此目的而编写的
(8)我不需要改变变量。我只需要以编程方式在Dalvik堆栈上需要变量magicNumber的地址,而无需使用任何工具或更改Dalvik源代码
我想知道Dalvik用于存储其堆栈的/ proc / pid / task / tid / maps的哪些内存区域。
正如你在paper1中看到的那样,作者在第B.2节第4行中没有解释他们用来为procs / maps分配哪些内存区域的开始和结束变量的值。
答案 0 :(得分:3)
您似乎正在尝试打开/proc/[pid]/task/[tid]/maps
,浏览地图,并手动扫描每个地址范围以查找幻数。撇开您在该目录中打开task
目录而不是maps
魔术文件的事实,这种方法存在一些问题:
findMagicNumber
方法返回,堆栈分配的变量将消失。如果再次调用该方法,则不能保证在同一个地方。我也不确定你为什么要调用mprotect()
,而不是简单地跳过那些不可读的片段。您将权限强制为只读,禁用写入和执行权限,这将导致您在执行的代码块执行权限被禁用时崩溃,或者当线程尝试执行时接触它的堆栈。
access()
系统调用采用文件名,而不是内存地址,并报告文件权限。我不认为这个地址是有效的&#34;打电话,但既然你只是对地图条目进行了扫描,那么你就不需要了。
查找和更改局部变量值的唯一可靠方法是使用JDWP调试接口。编译器和调试器支持协同工作,可以对局部变量进行可靠的读写访问。
总结一下:代码严重破坏,方法不健全。你想解决什么问题?