准备工作
- IDA 工具
- jadx-gui
先刨析java 层逻辑
读写app,native做了加密
package com.qxc.testnative3;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
Button btnRead;
Button btnWrite;
EditText etData;
public native String read(String str); //在native 实现
public native void write(String str, String str2); //在native 实现
static {
System.loadLibrary("native-lib");
}
/* access modifiers changed from: protected */
@Override // androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.etData = (EditText) findViewById(R.id.et_data);
this.btnWrite = (Button) findViewById(R.id.btn_write);
this.btnRead = (Button) findViewById(R.id.btn_read);
final String filePath = getFilesDir().getPath() + "/test";
this.btnWrite.setOnClickListener(new View.OnClickListener() {
/* class com.qxc.testnative3.MainActivity.AnonymousClass1 */
public void onClick(View v) {
MainActivity.this.write(filePath, MainActivity.this.etData.getText().toString());
}
});
this.btnRead.setOnClickListener(new View.OnClickListener() {
/* class com.qxc.testnative3.MainActivity.AnonymousClass2 */
public void onClick(View v) {
Toast.makeText(MainActivity.this, MainActivity.this.read(filePath), 1).show();
}
});
}
}
不是静态注册,这边肯定就是动态注册。
动态注册调用JNI_OnLoad这个函数进行动态注册
jint JNI_OnLoad(JavaVM *vm, void *reserved);
加载本机库时VM 调用JNI_OnLoad(例如,通过System.loadLibrary)。 JNI_OnLoad必须返回本机库所需的 JNI 版本。
为了使用任何新的 JNI 函数,本机库必须导出一个JNI_OnLoad返回 JNI_VERSION_1_2. 如果原生库不导出JNI_OnLoad函数,VM 会假定该库只需要 JNI 版本JNI_VERSION_1_1。如果 VM 无法识别返回的版本号 JNI_OnLoad,则无法加载本机库。
我们直接双击更过去,JNI_OnLoad函数,然后f5转成伪c代码
signed int __fastcall JNI_OnLoad(_JavaVM *a1)
{
int v1; // r0
signed int result; // r0
signed int v3; // [sp+20h] [bp-30h]
_JNIEnv *v4; // [sp+24h] [bp-2Ch]
__int64 v5; // [sp+28h] [bp-28h]
__int64 v6; // [sp+30h] [bp-20h]
__int64 v7; // [sp+38h] [bp-18h]
int v8; // [sp+44h] [bp-Ch]
v4 = 0;
if ( _JavaVM::GetEnv(a1, (void **)&v4, 65542) )
{
v3 = -1;
}
else
{
v5 = *(_QWORD *)off_176C8;
v6 = *(_QWORD *)&off_176D0;
v7 = *(_QWORD *)&off_176D8;
v1 = _JNIEnv::FindClass(v4, "com/qxc/testnative3/MainActivity");
if ( _JNIEnv::RegisterNatives(v4, v1, &v5, 2) )
v3 = -1;
else
v3 = 65542;
}
result = v3;
if ( _stack_chk_guard == v8 )
result = v3;
return result;
}
/**
* 动态注册
* @param vm 虚拟机对象
* @return
*/
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*){
jint result = -1;
JNIEnv *env = NULL;
//通过虚拟机对象获得环境对象
if(vm->GetEnv((void**)&env,JNI_VERSION_1_6)!=JNI_OK){
return result;
}
//定义java native函数与c native实现函数的映射关系
const JNINativeMethod method[] = {
{"write","(Ljava/lang/String;Ljava/lang/String;)V",(void *)TestWrite},
{"read","(Ljava/lang/String;)Ljava/lang/String;",(void *)TestRead}
};
//找到java类
jclass jclassName = env->FindClass("com/qxc/testnative3/MainActivity");
//执行动态注册
jint ret = env->RegisterNatives(jclassName,method,2);
if(ret != JNI_OK){
return result;
}
return JNI_VERSION_1_6;
汇编和jni 对应关系!
①为 :写方法
②为:读取方法
双击任何一个方法都可以跳转到对应的函数,然后f5
FILE *__fastcall TestWrite(int a1, int a2, int a3, int a4)
{
char *filename; // ST3C_4
int v5; // ST28_4
FILE *result; // r0
int v7; // ST34_4
char *s; // ST58_4
size_t size; // ST64_4
int v10; // r3
FILE *stream; // [sp+38h] [bp-40h]
int v12; // [sp+40h] [bp-38h]
int v13; // [sp+4Ch] [bp-2Ch]
v13 = a1;
v12 = a4;
filename = (char *)_JNIEnv::GetStringUTFChars(a1, a3);
v5 = _JNIEnv::GetStringUTFChars(v13, v12);
_android_log_print(4, "JNIDemo", &unk_14121, v5);
result = fopen(filename, (const char *)&unk_14145);
stream = result;
if ( result )
{
_android_log_print(4, "JNIDemo", &unk_14147, "JNIDemo");
v7 = encrypt(v13, v12);
s = (char *)_JNIEnv::GetStringUTFChars(v13, v7);
_android_log_print(4, "JNIDemo", &unk_1415D, s);
size = strlen(s);
fwrite(s, size, 1u, stream);
fclose(stream);
result = (FILE *)_android_log_print(4, "JNIDemo", &unk_14175, v10);
}
return result;
}
我这边是写的这个方法
extern "C" void
TestWrite(JNIEnv *env, jobject, jstring filePath, jstring fileContent){
const char *jPath = env->GetStringUTFChars(filePath,NULL);
LOGI("准备写入文件...,数据:%s",env->GetStringUTFChars(fileContent,NULL));
//打开文件
FILE *pFile = fopen(jPath,"w");
if(pFile == NULL){
return;
}
LOGI("文件打开成功...");
//数据加密
jstring enFileContent = encrypt(env,fileContent);
const char *jFileContent = env->GetStringUTFChars(enFileContent,NULL);
LOGI("加密后的数据:%s",jFileContent);
//数据写入
fwrite(jFileContent,strlen(jFileContent),1,pFile);
//关闭文件
fclose(pFile);
LOGI("文件写入成功,关闭文件");
}
伪代码和jni 对比 区别不大把!
static char* key = "1234567887654321";
/**
* AES加密功能
* @param env 环境对象
* @param fileContent 待加密的文件内容
* @return 加密后的密文数据
*/
jstring encrypt(JNIEnv *env, jstring fileContent){
//获得java端class
jclass cls = env->FindClass("com/qxc/testnative3/AES");
if(cls == NULL){
return NULL;
}
//通过class获取方法id
jmethodID methodidEncrypt = env->GetStaticMethodID(cls,"encrypt","(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
if(methodidEncrypt == NULL){
return NULL;
}
//char* 转jstring
jstring keyAes = env->NewStringUTF(key);
//调用AES加密方法
return (jstring)env->CallStaticObjectMethod(cls,methodidEncrypt,fileContent,keyAes);
}
encrypt这个方法就找到了!我们在学习so算法逆向,多学习ndk开发!
我是任雪飘。一个技术渣渣,请关注,不定期更新