#include "com_example_jni02_CallSo.h"
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <elf.h>
#include <sys/mman.h>
#include <Android/log.h>
//这里对 Java_com_example_jni02_CallSo_plus这个方法进行加密保护
jint JNICALL Java_com_example_jni02_CallSo_plus(JNIEnv* env, jobject obj, jint a, jint b)  __attribute__((section (".mytext")));
JNIEXPORT jstring JNICALL Java_com_example_jni02_CallSo_getString
  (JNIEnv* env, jobject obj){
	return (*env)->NewStringUTF(env,"Hello");
}
JNIEXPORT jint JNICALL Java_com_example_jni02_CallSo_plus
  (JNIEnv* env, jobject obj, jint a, jint b){

	return a+b;
}
//在调用so文件进行解密
void init_Java_com_example_jni02_CallSo_plus() __attribute__((constructor));
unsigned long getLibAddr();

void init_Java_com_example_jni02_CallSo_plus(){
	char name[15];
	unsigned int nblock;
	unsigned int nsize;
	unsigned long base;
	unsigned long text_addr;
	unsigned int i;
	Elf32_Ehdr *ehdr;
	Elf32_Shdr *shdr;
	base=getLibAddr();
	ehdr=(Elf32_Ehdr *)base;
	text_addr=ehdr->e_shoff+base;
	nblock=ehdr->e_entry >>16;
	nsize=ehdr->e_entry&0xffff;
	__android_log_print(ANDROID_LOG_INFO, "JNITag", "nblock =  0x%d,nsize:%d", nblock,nsize);
    __android_log_print(ANDROID_LOG_INFO, "JNITag", "base =  0x%x", text_addr);
	printf("nblock = %d\n", nblock);
   //修改内存权限
	 if(mprotect((void *) (text_addr / PAGE_SIZE * PAGE_SIZE), 4096 * nsize, PROT_READ | PROT_EXEC | PROT_WRITE) != 0){
	   puts("mem privilege change failed");
	    __android_log_print(ANDROID_LOG_INFO, "JNITag", "mem privilege change failed");
	 }
	 //进行解密,是针对加密算法的
	 for(i=0;i<nblock;i++){
		 char *addr=(char*)(text_addr+i);
		 *addr=~(*addr);
	 }
	  if(mprotect((void *) (text_addr / PAGE_SIZE * PAGE_SIZE), 4096 * nsize, PROT_READ | PROT_EXEC) != 0){
	    puts("mem privilege change failed");
	  }
	  puts("Decrypt success");
}
//获取到SO文件加载到内存中的起始地址,只有找到起始地址才能够进行解密;
unsigned long getLibAddr(){
	unsigned long ret=0;
	char name[]="libaddcomputer.so";
	char buf[4096];
	char *temp;
	int pid;
	FILE *fp;
	pid=getpid();
	sprintf(buf,"/proc/%d/maps",pid);
	fp=fopen(buf,"r");
	if(fp==NULL){
		puts("open failed");
		goto _error;
	}
	while (fgets(buf,sizeof(buf),fp)){
		if(strstr(buf,name)){
			temp = strtok(buf, "-");
			ret = strtoul(temp, NULL, 16);
			break;
		}
	}
	_error:
	  fclose(fp);
	  return ret;
}
首先看到的是getLibAddr()这个函数:在介绍这个函数之前首先了解一个内存映射问题:
和Linux一样,Android提供了基于/proc的“伪文件”系统来作为查看用户进程内存映像的接口(cat /proc/pid/maps)。可以说,这是Android系统内核层开放给用户层关于进程内存信息的一扇窗户。通过它,我们可以查看到当前进程空间的内存映射情况,模块加载情况以及虚拟地址和内存读写执行(rwxp)属性等。接下来包括内存权限的修改以及函数的解密算法,最后包括内存权限的修改回去,应该都比较好理解。ok,以上编写完以后就编译生成.so文件。
private static void encodeSection(byte[] fileByteArys){
		//读取String Section段
		System.out.println();
		
		int string_section_index = Utils.byte2Short(type_32.hdr.e_shstrndx);
		elf32_shdr shdr = type_32.shdrList.get(string_section_index);
		int size = Utils.byte2Int(shdr.sh_size);
		int offset = Utils.byte2Int(shdr.sh_offset);

		int mySectionOffset=0,mySectionSize=0;
		for(elf32_shdr temp : type_32.shdrList){
			int sectionNameOffset = offset+Utils.byte2Int(temp.sh_name);
			if(Utils.isEqualByteAry(fileByteArys, sectionNameOffset, encodeSectionName)){
				//这里需要读取section段然后进行数据加密
				mySectionOffset = Utils.byte2Int(temp.sh_offset);
				mySectionSize = Utils.byte2Int(temp.sh_size);
				byte[] sectionAry = Utils.copyBytes(fileByteArys, mySectionOffset, mySectionSize);
				for(int i=0;i<sectionAry.length;i++){
					//sectionAry[i] = (byte)(sectionAry[i] ^ 0xFF);
					sectionAry[i]=(byte) ~sectionAry[i];
				}
				Utils.replaceByteAry(fileByteArys, mySectionOffset, sectionAry);
			}
		}

		//修改Elf Header中的entry和offset值
		int nSize = mySectionSize/4096 + (mySectionSize%4096 == 0 ? 0 : 1);
		byte[] entry = new byte[4];
		entry = Utils.int2Byte((mySectionSize<<16) + nSize);
		Utils.replaceByteAry(fileByteArys, 24, entry);
		byte[] offsetAry = new byte[4];
		offsetAry = Utils.int2Byte(mySectionOffset);
		Utils.replaceByteAry(fileByteArys, 32, offsetAry);
	}