转载

Unity3D研究院之Android二次加密.so二次加密DLL(八十二)

上文中说了怎么给DLL加密来防止别人反编译你的C#代码。 Unity3D研究院之Android加密DLL与破解DLL .SO(八十一) 文章的最后我们发现IDA PRO神器可以解开libmono从而查到你的解密算法,这样你的C#代码又会被别人轻易的拿到。

这两天我就一直在寻找怎样才能更好的保护代码。终于找到了加密so的办法,此法我觉得防小白觉对够用。大神恐怕还是能解开,但是我觉得这就够了。我已经在项目中测试通过,也欢迎大家也能加入一起来测试的队伍。

在啰嗦一句在不远的将来可能我们也不用这么做了, 因为很快unity就全线l2cpp了。但是我觉得等真正稳定恐怕还有很多路要走,所以估计大部分正在开发的项目不会冒这个险升级。

阅读下面之前请大家先看一下这篇大神的文章。 http://bbs.pediy.com/showthread.php?t=191649 文章写的很清晰。但是坦白说看了半天我没怎么看懂,逆向工程真是一门深奥的学问。。主要还是技术关注领域不在这里。文章的最后有作者给出的源码,大家记得下载下来。然后我就开始说我是怎么把这个加在unity3d上的。还有我遇到了那些坑。

它的例子工程下载解压后,开始对shellAdder1.c进行编译,编译的方法是

gcc - o encry shellAdder1 . c

我开始编译的时候老通不过,提示缺少 elf.h 文件,我看了一下,其实就是少了一些结构体和类型的声明,把下面代码拷贝到shellAdder1.c里面即可。Main函数上面添加如下代码。

#include <stdio.h>

#include <fcntl.h>

#include <stdlib.h>

#include <string.h>

/* 32-bit ELF base types. */

typedef unsigned int Elf32_Addr ;

typedef unsigned short Elf32_Half ;

typedef unsigned int Elf32_Off ;

typedef signed int Elf32_Sword ;

typedef unsigned int Elf32_Word ;

#define EI_NIDENT 16

/*

* ELF header.

*/

typedef struct {

unsigned char    e_ident [ EI_NIDENT ] ;    /* File identification. */

Elf32_Half   e_type ;      /* File type. */

Elf32_Half   e_machine ;    /* Machine architecture. */

Elf32_Word   e_version ;    /* ELF format version. */

Elf32_Addr   e_entry ;    /* Entry point. */

Elf32_Off   e_phoff ;    /* Program header file offset. */

Elf32_Off   e_shoff ;    /* Section header file offset. */

Elf32_Word   e_flags ;    /* Architecture-specific flags. */

Elf32_Half   e_ehsize ;    /* Size of ELF header in bytes. */

Elf32_Half   e_phentsize ;    /* Size of program header entry. */

Elf32_Half   e_phnum ;    /* Number of program header entries. */

Elf32_Half   e_shentsize ;    /* Size of section header entry. */

Elf32_Half   e_shnum ;    /* Number of section header entries. */

Elf32_Half   e_shstrndx ;    /* Section name strings section. */

} Elf32_Ehdr ;

/*

* Section header.

*/

typedef struct {

Elf32_Word   sh_name ;    /* Section name (index into the

section header string table). */

Elf32_Word   sh_type ;    /* Section type. */

Elf32_Word   sh_flags ; /* Section flags. */

Elf32_Addr   sh_addr ;    /* Address in memory image. */

Elf32_Off sh_offset ;    /* Offset in file. */

Elf32_Word   sh_size ;    /* Size in bytes. */

Elf32_Word   sh_link ;    /* Index of a related section. */

Elf32_Word   sh_info ;    /* Depends on section type. */

Elf32_Word   sh_addralign ; /* Alignment in bytes. */

Elf32_Word   sh_entsize ; /* Size of each entry in section. */

} Elf32_Shdr ;

最终shellAdder1将编译成一个名叫encry的可执行文件, 用来给libmono进行加密。那么加密算法必然是要写在shellAdder1.c里面,作者给出的是取反你也可以改成自己需要的算法。至于c代码是什么意思,我相信 这篇文章已经写的是非常的全面了 http://0nly3nd.sinaapp.com/?p=695

然后执行 encry libmono.so 就会把libmono.so里 名叫 mytext 的断 进行加密,你要觉得这个名子不好也可以换一个断名,加密后的libmono.so文件会替换原有的。

接着到mono/metadata/image.c里来编写解密.so断的代码。把下面这段代码拷贝到image.c的最上面,关键的两个地方我已添加注释了。

//SO---------------加密----------------------

#include <sys/types.h>

#include <elf.h>

#include <sys/mman.h>

//注意上面说解密算法里面的断.mytext就是这里,

//这里把getKey进行了加密,这样对方拿不到你的密钥都没法破解你的dll了

int getKey ( ) __attribute__ ( ( section ( ".mytext" ) ) ) ;

int getKey ( ) {

return 2048 ;

} ;

//这里就是.so初始化的时候,这里进行mytext断的解密工作

void init_getKey ( ) __attribute__ ( ( constructor ) ) ;

unsigned long getLibAddr ( ) ;

void init_getKey ( ) {

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 ;

g_message ( "momo: nblock = %d/n" , nblock ) ;

if ( mprotect ( ( void * ) base , 4096 * nsize , PROT_READ | PROT_EXEC | PROT_WRITE ) != 0 ) {

g_message ( "momo: mem privilege change failed" ) ;

}

//注意这里就是解密算法, 要和加密算法完全逆向才行不然就解不开了。

for ( i = 0 ; i < nblock ; i ++ ) {   

char * addr = ( char * ) ( text_addr + i ) ;

* addr = ~ ( * addr ) ;

}

if ( mprotect ( ( void * ) base , 4096 * nsize , PROT_READ | PROT_EXEC ) != 0 ) {

g_message ( "momo: mem privilege change failed" ) ;

}

g_message ( "momo: Decrypt success" ) ;

}

unsigned long getLibAddr ( ) {

unsigned long ret = 0 ;

char name [ ] = "libmono.so" ;

char buf [ 4096 ] , * temp ;

int pid ;

FILE * fp ;

pid = getpid ( ) ;

sprintf ( buf , "/proc/%d/maps" , pid ) ;

fp = fopen ( buf , "r" ) ;

if ( fp == NULL )

{

g_message ( "momo: 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 ;

}

//SO---------------加密----------------------

然后在mono_image_open_from_data_with_name方法里

if ( strstr ( name , "Assembly-CSharp.dll" ) ) {

//这里就能取到密钥,那么这个函数被加密了。

//IDA就看不到它了

g_message ( "momo: key = %d/n" , getKey ( ) ) ;

}

密钥被保护了,代码修改完就是开始编译mono吧。编译完用刚刚我们说过的方法来执行 encry libmono.so  然后把libmono拷贝到项目里打包android就行了。

可以测试一下加密的效果。用Ida 打开。这里的函数已经打不开了

Unity3D研究院之Android二次加密.so二次加密DLL(八十二)

这段密钥进行了保护那么就可以随意的做加密算法了。

Unity3D研究院之Android二次加密.so二次加密DLL(八十二)

我相信这个方法还是存在漏洞,肯定也有大神能破解。也希望各位大神不吝赐教,谢谢啦。使用上有问题欢迎在下面留言大家可以一起讨论。

正文到此结束
Loading...