Unity Android 加密 so
前言:
首先感謝MOMO提供的資料和FB各位的悉心教導。
http://www.xuanyusong.com/archives/3571
編譯環境:Mac電腦
正文:
1:IDA 反編譯神器
IDA是反編譯的一種工具,我隻簡單用了一天而已,也就是用來檢視so反編譯,其中需要用到F5插件,下載下傳一個然後簡單配置下就可以了。
http://www.h4ck.org.cn/2014/08/ida-pro-6-5-with-hex-rays-x86-decompiler-v1-5-and-hex-rays-arm-decompiler-1-7/
http://forum.cnsec.org/thread-93771-1-1.html
配置方法:下載下傳完畢解壓,解壓後裡面有唯一一個壓縮包,解壓并放到相應的檔案夾下
然後打開我們編譯的libmono.so
找到mono_image_open_from_data_width_name 方法,然後點選F5 解密算法就破解了
借用下MoMo的圖
2:shellAdder1.c編譯檔案
執行編譯shellAdder1.c檔案,編譯方法
gcc -o encry shellAdder1.c
shellAdder1.c檔案内容
#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;
int main(int argc, char** argv){
char target_section[] = ".mytext";
char *shstr = NULL;
char *content = NULL;
Elf32_Ehdr ehdr;
Elf32_Shdr shdr;
int i;
unsigned int base, length;
unsigned short nblock;
unsigned short nsize;
unsigned char block_size = 16;
int fd;
if(argc < 2){
puts("Input .so file");
return -1;
}
fd = open(argv[1], O_RDWR);
if(fd < 0){
printf("open %s failed\n", argv[1]);
goto _error;
}
if(read(fd, &ehdr, sizeof(Elf32_Ehdr)) != sizeof(Elf32_Ehdr)){
puts("Read ELF header error");
goto _error;
}
lseek(fd, ehdr.e_shoff + sizeof(Elf32_Shdr) * ehdr.e_shstrndx, SEEK_SET);
if(read(fd, &shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)){
puts("Read ELF section string table error");
goto _error;
}
if((shstr = (char *) malloc(shdr.sh_size)) == NULL){
puts("Malloc space for section string table failed");
goto _error;
}
lseek(fd, shdr.sh_offset, SEEK_SET);
if(read(fd, shstr, shdr.sh_size) != shdr.sh_size){
puts("Read string table failed");
goto _error;
}
lseek(fd, ehdr.e_shoff, SEEK_SET);
for(i = 0; i < ehdr.e_shnum; i++){
if(read(fd, &shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)){
puts("Find section .text procedure failed");
goto _error;
}
if(strcmp(shstr + shdr.sh_name, target_section) == 0){
base = shdr.sh_offset;
length = shdr.sh_size;
printf("Find section %s\n", target_section);
break;
}
}
lseek(fd, base, SEEK_SET);
content = (char*) malloc(length);
if(content == NULL){
puts("Malloc space for content failed");
goto _error;
}
if(read(fd, content, length) != length){
puts("Read section .text failed");
goto _error;
}
nblock = length / block_size;
nsize = base / 4096 + (base % 4096 == 0 ? 0 : 1);
printf("base = %d, length = %d\n", base, length);
printf("nblock = %d, nsize = %d\n", nblock, nsize);
ehdr.e_entry = (length << 16) + nsize;
ehdr.e_shoff = base;
for(i=0;i<length;i++){
content[i] = ~content[i];
}
lseek(fd, 0, SEEK_SET);
if(write(fd, &ehdr, sizeof(Elf32_Ehdr)) != sizeof(Elf32_Ehdr)){
puts("Write ELFhead to .so failed");
goto _error;
}
lseek(fd, base, SEEK_SET);
if(write(fd, content, length) != length){
puts("Write modified content to .so failed");
goto _error;
}
puts("Completed");
_error:
free(content);
free(shstr);
close(fd);
return 0;
}
最終shellAdder1将編譯成一個名叫encry的可執行檔案, 用來給libmono進行加密。那麼加密算法必然是要寫在shellAdder1.c裡面,作者給出的是取反你也可以改成自己需要的算法。
然後執行 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(char *data) __attribute__((section (".mytext")));
int getKey(char *data){
data[0]-=1;
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方法裡
1 2 3 4 5 | if(strstr(name,"Assembly-CSharp.dll")){ //這裡就能取到密鑰,那麼這個函數被加密了。 //IDA就看不到它大 g_message("momo: key = %d\n", getKey(data));//data的資料在getKey方法裡面進行解密 } |
密鑰被保護了,代碼修改完就是開始編譯mono吧。編譯完用剛剛我們說過的方法來執行 encry libmono.so 然後把libmono拷貝到項目裡打包android就行了
可以測試一下加密的效果。用Ida 打開。這裡的函數已經打不開了
借用兩張圖