首页
社区
课程
招聘
[求助]Dex内存加载的Native实现过程中出现的问题
发表于: 2014-1-8 11:57 26790

[求助]Dex内存加载的Native实现过程中出现的问题

2014-1-8 11:57
26790
在参考http://blog.csdn.net/androidsecurity/article/details/9674251这篇文章实现dex内存加载的native代码时,遇到了一些问题:
首先我按照文章上给的方法获取到了Dalvik_dalvik_system_DexFile_openDexFile_bytearray方法指针,但是在调用这个函数时,参数构造令我比较疑惑。在Android源码中这个函数原型为
static void Dalvik_dalvik_system_DexFile_openDexFile_bytearray(const u4* args,  JValue* pResult)
{
     ArrayObject* fileContentsObj = (ArrayObject*) args[0];
     ……
}
这个函数为Dalvik虚拟机内部的jni函数(DalvikNativeFunc类型),参数中的args指针中指向具体的参数列表,然后在函数内部进行转换。我们想调用这个函数的话也就是需要构造一个这样的指针,但是jni调用时函数为
JNIEXPORT jint JNICALL Java_com_xxx_xxx_xxx_openDexFileByteArray (JNIEnv *env, jobject object, jbyteArray dexArray)
这个函数为另一种jni函数形式(DalvikBridgeFunc类型),传入的参数类型为jbyteArray,jbyteArray在jni.h里定义为void *类型的指针,理论上其指向的内存地址存放的应该是一个ArrayObject结构体的指针,但是在将jbyteArray转换成ArrayObject指针后,发现两者并不等价。所以在这点上我十分疑惑,纠结了很久,所以在此求助,希望大家予以帮助哈~

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (29)
雪    币: 269
活跃值: (25)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
2
GetByteArrayElements,调一下就行了。

其实吧,自己从native层把文件数据读出来构造一个ArrayObject即可,何必多次一举从上层传下来?

或者干脆直接把dex数据加密之后放到.so文件的数据区,能挡住一些小白来分析你的dex了
2014-1-13 11:25
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
为了方便,我的dex文件是从java层读到内存空间里的,后来我自己也想着去构造一个ArrayObject,也调用了GetByteArrayElements,把得到的指针存放到ArrayObject里,但是ArrayObject的定义里有句话比较困惑,贴一下源码:

struct Object {
    /* ptr to class object */
    ClassObject*    clazz;

    /*
     * A word containing either a "thin" lock or a "fat" monitor.  See
     * the comments in Sync.c for a description of its layout.
     */
    u4              lock;
};

/*
 * Array objects have these additional fields.
 *
 * We don't currently store the size of each element.  Usually it's implied
 * by the instruction.  If necessary, the width can be derived from
 * the first char of obj->clazz->descriptor. */

struct ArrayObject : Object {
    /* number of elements; immutable after init */
    u4              length;

    /*
    * Array contents; actual size is (length * sizeof(type)).  [COLOR="Red"]This is
     * declared as u8 so that the compiler inserts any necessary padding
     * (e.g. for EABI); the actual allocation may be smaller than 8 bytes.[/COLOR]
     */
    u8              contents[1];
};


源码里红字的部分解释了为什么存放内容的地方要定义成u8,但是看得很不明白,这里指针还要强制转换成u8类型的么?求解释=。=

ps.就算把dex加密后放到.so的数据区,那加载的时候还是要解密出来一个dex啊,不管是内存还是本地,总是防不住的。

2014-1-15 15:10
0
雪    币: 269
活跃值: (25)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
4
红字不懂,感觉是编译器优化相关的,猜测:如果定义成其他结构可能导致arrayByte在不同情况下编译出来结构大小不一样?

contents就是byte*,处理不定长结构时经常这么玩,lz是不是搞Java的,对类型太敏感了。。。

ps:对方会内存dump的话这种方案当然没效果,只能防一下只会jd-gui的门外汉~
2014-1-16 02:17
0
雪    币: 77
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
看错了……
2014-1-16 09:16
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
我是搞C的,Java只会一点,倒不是对类型敏感,只是觉得contents[1]定义为u8,那么构造的时候也要把指针给扩展成u8吧?不过听你这样说之后,让我觉得contents存放的只是一个指针,用法跟普通的指针一样,不用去管定义,自己构造的话就赋值为dex的地址就行了吧?之前跟一个哥们聊过,他的构造方法我觉得不对,但是他的代码竟然可以跑通,他是这样构造的:
u4 args[1] = { (u4)(length,byterray) };
openDexFile(args,&pResult);

其中bytearray直接是java层传进来的jbytearray类型的dex,我觉得很是奇怪。不过他的代码只能在arm架构上跑,变成armv7就不行了。我自己现在是这样构造的(参考的是Android2.3源码里的ArrayObject的C语言定义,不会C++的杯具):
typedef struct Object {
	void*      clazz;
	uint32_t   lock;
} Object;

typedef struct
{
	Object   ok;
	uint32_t length;
	uint32_t contents;
} ArrayObject;


然后对应的部分进行赋值,但是在调用openDexFile的时候就是不能通过,感觉还是ArrayObject的结构不太对,请问这样构造还是不对的么?还有,那个处理不定长结构为什么经常要这样定义呢,直接定义为一个指针指向一个buf不就好了么?不是很理解,还望指教!
2014-1-16 13:41
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
额,看什么看错了?
2014-1-16 13:42
0
雪    币: 269
活跃值: (25)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
8
[QUOTE=wfgfw;1255649]我是搞C的,Java只会一点,倒不是对类型敏感,只是觉得contents[1]定义为u8,那么构造的时候也要把指针给扩展成u8吧?不过听你这样说之后,让我觉得contents存放的只是一个指针,用法跟普通的指针一样,不用去管定义,自己构造的话就赋值为dex的地址就行了吧?之前跟一个哥们聊过,他的构造...[/QUOTE]

ArrayObject的内存布局:

+0  lenth  
+4  byte[0]
+5  byte[1]
+6  byte[2]
....

“直接定义为一个指针指向一个buf不就好了么” 的内存布局:

+0  lenth  
+4  ptr

我是觉得ArrayObject这样比较方便拷贝对象。

#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
    static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
        _ctype##Array jarr, jboolean* isCopy) \
    { \
        ScopedJniThreadState ts(env); \
        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); \
        pinPrimitiveArray(arrayObj); \
        _ctype* data = (_ctype*) (void*) arrayObj->contents; \
        if (isCopy != NULL) { \
            *isCopy = JNI_FALSE; \
        } \
        return data; \
    }


这个宏你可以看一下,这是GetByteArrayElements的实现。jbyteArray只是ArrayObject的一个线程相关的句柄,需要靠 dvmDecodeIndirectRef这个函数转化的。

你那哥们写的当然不对,他那个能在旧机器上成功是因为老机器上的dalvik上面jbyteArray和ArrayObject没有这一层映射关系,可以直接引用。

至于“在调用openDexFile的时候就是不能通过”,你还是看一下返回值和日志吧……
2014-1-16 15:35
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
[QUOTE=ReturnsMe;1255689]ArrayObject的内存布局:

+0  lenth  
+4  byte[0]
+5  byte[1]
+6  byte[2]
....

“直接定义为一个指针指向一个buf不就好了么” 的内存布局:

+0  lenth  
+4  ptr

我是觉得ArrayOb...[/QUOTE]

十分感谢大神解答,看完之后对ArrayObject的理解已经非常清晰了,但还有最后一个问题:这样的内存布局的话,本来contents就8个字节,却要用来存放那么大的dex,不怕数组越界么?不知道我这个问题问得对不对?
最后还是要再次感谢一下,看来自己要学的还差很多啊~
2014-1-18 22:43
0
雪    币: 269
活跃值: (25)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
10
前两天怒搞selinux都没来看雪。。

其实contents只是起到一个指针的作用,因此也就没有越界一说,真正分配内存时,分配的大小是依照byte的数量决定的。为ArrayOject分配内存时,显然不能malloc(sizeof(ArrayObject)); 而是要 malloc(size_byte + 4);来保证分配的内存足够。。
2014-1-21 23:25
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
完全明白了,再次感谢!
2014-1-22 09:09
0
雪    币: 216
活跃值: (65)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
请问楼主,ARM-V7下最后应该怎样把 jbyteArray,变成ArrayObject ?需要自己重新构造吗,还是有现在的函数可调用?
2014-9-19 17:55
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
既可以自己构造,也可以调用dvmDecodeIndirectRef这个函数~
2014-9-22 09:39
0
雪    币: 216
活跃值: (65)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
谢谢楼主!
1、
试着用按说的内存布展,在JAVA层构造一个buf:
+0  int : 指示后面DEX文件长度lenth  
+4  :写入DEX内容
+5  :...

将这个buf 传入 JIN层,类型转换为 jbyteArray ,名称为sArrayBuf,并调用OpenDexFile(u4* args, JValue* pResult)

char* pBuf = (char*)env-> GetByteArrayElements(sArrayBuf, NULL);
openDexFile((u4*)pBuf, &pResult);

但系统报错! 不知明白原因?楼主如何解决这个问题的!

2、你说的 dvmDecodeIndirectRef 函数,我在JNI.H中没找到定义,无法引用?不知这个函数在哪个文件中,需要include 哪个.h 文件吗?
2014-9-22 10:37
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
1、那个内存布局就是JNI中的ArrayObject的布局了,直接就在JNI里面当ArrayObject用,不是从Java层传递进去的。
2、在libdvm.so里,需要dlopen调用。
2014-9-22 11:02
0
雪    币: 216
活跃值: (65)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
看了下内存,感觉从JAVA层构造后传入与JNI里构造在内存上看了下效果一样!
这么说也说不清,给楼主发了消息,有空单聊一下吧!谢谢!
2014-9-22 11:33
0
雪    币: 275
活跃值: (259)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
17
不好意思,,想问问楼上的potop大哥,,,,谢谢啊,
2014-10-2 21:37
0
雪    币: 212
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
我也遇到了类似的问题 楼主能给个qq么 求教
2014-12-2 21:17
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
可以问题发在这里,一起讨论嘛~
2014-12-8 11:03
0
雪    币: 212
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
现在我的问题和你之前遇到的一样 opendexfile过不去 我觉得应该就是arrayobject数组定义的问题 我用的数组定义是
typedef struct Object {
  void*      clazz;
  uint32_t   lock;
} Object;

typedef struct
{
  Object   ok;
  uint32_t length;
  uint32_t contents;
} ArrayObject;
是不是这个定义不对?
2014-12-10 21:28
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
这样是不太对的,你仔细看上面的帖子,其实ArrayObject的那个content并不是一个指针,而只是一个占位符,代表着一段缓冲区,具体的大小根据这个数组的真正大小进行分配,malloc(12+数组长度)。
2014-12-12 13:46
0
雪    币: 212
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
哦 明白了 分配的长度我有点小疑问,我用ida跟进去看了下 opendexfile里面是拷贝数据是从+0x10的位置开始的,是不是应该是分配malloc(16+数组长度)呢?
2014-12-13 09:40
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
这个不是特别清楚了诶,看结构定义应该是12~
2014-12-15 17:20
0
雪    币: 212
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
结构体最后一个字段是u8型 应该占8个字节
2014-12-16 10:21
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
从contents开始就是正式存放数组内容的地方了,12是指contents前面几个结构体成员的大小,所以我觉得应该是12+数组长度,这个你可以验证一下=。=
2014-12-16 11:00
0
游客
登录 | 注册 方可回帖
返回
//