从入门到自闭——安卓逆向学习日志(二)解决.so文件逆向看不懂问题

4 篇文章 2 订阅
1 篇文章 0 订阅

本文意在解决进行安卓逆向时初学者(已掌握c语言)容易遇到的部分困难,仅以逆向者的角度分析问题,因此不讨论开发技术细节。
如果解决不了,也请不要打我

JNI(Java Native Interface)

JNI规范,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。从而实现C/C++代码在JVM虚拟机中运行。而在JAVA代码中声明native方法,即可调用.so文件中的函数方法。

JNIEnv和jobject

话不多说,现看开发者要生成一个.so文件,通常要写的代码:

JNIEXPORT void JNICALL Java_com_jni_demo_Test(JNIEnv * env,jobject obj)
{
	//代码部分
}

看到这里你小小的脑袋可能和我一样充满大大的疑惑

  1. JNIEXPORT和JNICALL的用途在于标识当前的函数是可以在JAVA层中被调用。
  2. 不同于JAVA层中用“.”进行标识包,这里使用“_”来标识。
  3. 本次函数中的两个参数为编写时的固定参数,通过传入JNIEnv*可以对JAVA层的代码进行操作,例如调用JAVA层的方法,获取对象等。
  4. 对于jobject参数,我们则需要到JAVA层中观察代码
public class TestJava{
	//..........
	public static native int Test(String arg0) {}
	//..........
}

又或者是

public class TestJava{
	//..........
	public native int Test(String arg0) {}
	//..........
}

当native方法是static时,obj就代表native方法的类的class对象实例,不是static时,obj则代表native方法的类实例。

**此时需要注意的是,*作为逆向者,当使用IDA时,在JAVA层声明时的参数个数会在IDA中莫名奇妙的多两个,其实也就是本文中提到的JNIEnv,jobject。然而IDA并不吃这一套 然而此处IDA会默认为int a1和int a2,导致部分代码中的方法非常难识别,比如:
在这里插入图片描述
在这里插入图片描述因此,需要手动修改参数的类型,选中参数,修改a1和a2的类型分别为JNIEnv*和jobject。
在这里插入图片描述
函数也自动变得容易理解了。

但由于JAVA与C++仍是两种类型,二者数据类型存在如下的对应关系:
在这里插入图片描述当IDA进行逆向后,会将数据类型还原成本地类型。

关于函数GetStringUTFChars,由于JAVA使用的是Unicode编码,因此进行一种转换 ,此处作为逆向者仅理解为将该函数的第二个参数转换为char *类型即可。ReleaseStringUTFChars即为将当前的字符串的内存释放。

关于函数memset:在一段内存块中填充某个给定的值

memset(void *buffer, int c, int count)
其中的buffer为指针,c为填充的内容,count为填充的前几个字符

说人话就是,将buffer对应的字符串的前count的字符填充为c
类似memset(buffer, 0, count)的用法,是将buffer对应的内存清空

关于函数memcpy

memcpy(void *s1,void *s2,size_t length)
向s1中复制s2的前length个字符

补一个函数__OFSUB__表示x-y是否溢出,溢出返回1,没有溢出返回0。

jclass类,以及C++获取Java类

获取jclass的几种方法:

  • 获取Java中的类
jclass FindClass(const char* clsName)
//通过传入类的完整名称获得jclass,
//如:
jclass Java_class = env->FindClass("com/demo/test")
  • 通过jobject获取Java中的类
jclass GetObjectClass(jobject obj)
//通过对象实例来获取类,类似Java中的getClass方法
//例:
jclass ax_list_jclass = env->GetObjectClass(String);
  • 获取父类对象
jclass GetSuperClass(jclass obj)
//获取obj的父类对象,不再举例

获取JAVA属性和方法

  • 获取属性/方法,前获取jfieldID/jmethodID
env->GetFiedID(jclass clazz,const char *name,const char *sign)
//其中clazz为属性对应的jcalss对象,name为获取属性名称,sign为字段的签名
//sign可由命令javap -s -p Demo.class命令获取,签名和数据类型相对应,(传入的参数)
env->GetMethodID(jclass clazz,const char *name,const char *sign)
//GetStaticMethodID和GetStaticFiedID与上面两种方法区别不大,不再赘述。

在这里插入图片描述作为逆向者,此处还是以IDA反汇编结果为例,下图中的GetMethodID方法参数中,v3为JNIEnv* a1,v4为findClass获得的结果,即对应上述jclass,
在这里插入图片描述
在这里插入图片描述

C++中调用Java层的函数

调用方法:
env->Call<Type>Method(jobject,jFieldID,传入参数)
此外还有静态函数的调用方法:
env->CallStatic<Type>Method()

获取Java对象

jobject NewObject(jclass clazz,jmethodID ID,构造函数的其他参数)
//获取构造函数的jmethodID时,所用的签名为()V,方法名称为<init>
//另一种方法:
env->AllocObject(jclass clazz)
//作为非开发人员的逆向者,得知这两种方法可以获得java对象即可

对Java中字符串的操作

//获取字符串长度
jsize GetStringLength(jstring jstr)
//jstring 转换为 char* 
env->GetStringRegion(jstring jstr,jsize start,jsize length,char* result)
//const jchar*转为jstring对象
jobject NewString(const jchar* jstr,int size)
//jstring 转换为 const jchar*
GetStringChars(jstring jstr,jboolean* copied)
//jboolean类型的指针用于标识是否对字符串进行了复制,已复制则设置为JNI_TRUE,否则为JNI_FALSE。若传入的指针为NULL,则不关心是否复制。
ReleaseStringChars(jstring jstr,const jcahr* jstr)
//用于对字符串释放内存
GetStringUTFChars()
用法与上方法类似,释放方法:
ReleaseStringUTFChars()

获取Java中数组的方法

//获取数组对象,其中数组类型,如jintArray
j<Type>Array array=(j<Type>Array)env->GetObjectField(jobject obj,jfieldID jarray_ID)
//获取数组对象指针
j<Type>* array=env->Get<type>ArrayElements(jfieldID jarray_ID,NULL)
//使用C++数组给Java数组进行赋值
Set<Type>ArrayRegion(<Type>Array arr,jsize start, jsize len,const <Type>* buffer)
//将Java数组中的值赋值给buffer
Get<Type>ArrayRegion(<Type>Array array,jsize start,jsize len,const <Type>*buffer)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值