从本机代码

时间:2015-12-31 13:56:37

标签: android java-native-interface stack native dalvik

我正在使用针对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分配哪些内存区域的开始和结束变量的值。

1 个答案:

答案 0 :(得分:3)

您似乎正在尝试打开/proc/[pid]/task/[tid]/maps,浏览地图,并手动扫描每个地址范围以查找幻数。撇开您在该目录中打开task目录而不是maps魔术文件的事实,这种方法存在一些问题:

  1. 您无法保证该值是唯一的。如果它出现在内存中的其他位置 - 也许是因为该值存储在两个不同的Dalvik寄存器中 - 您将处于错误的位置。如果JIT编译器编译了这段代码,你就不知道&#34;是否活跃了#34;值将位于托管堆栈或本机堆栈上的溢出寄存器中。
  2. 您正在搜索0x14,这不是您神奇数字的一部分。
  3. 您正在扫描创建它的线程上的局部变量。即使您找到它并且可以更改它,一旦findMagicNumber方法返回,堆栈分配的变量将消失。如果再次调用该方法,则不能保证在同一个地方。
  4. 如果您希望在附近找到相关变量,那么如果JIT编译器重新排列内容,您将再次遇到麻烦。
  5. ART几乎可以提前编译所有内容,因此在那里使用它的可能性更小。
  6. 我也不确定你为什么要调用mprotect(),而不是简单地跳过那些不可读的片段。您将权限强制为只读,禁用写入和执行权限,这将导致您在执行的代码块执行权限被禁用时崩溃,或者当线程尝试执行时接触它的堆栈。

    access()系统调用采用文件名,而不是内存地址,并报告文件权限。我不认为这个地址是有效的&#34;打电话,但既然你只是对地图条目进行了扫描,那么你就不需要了。

    查找和更改局部变量值的唯一可靠方法是使用JDWP调试接口。编译器和调试器支持协同工作,可以对局部变量进行可靠的读写访问。

    总结一下:代码严重破坏,方法不健全。你想解决什么问题?