天天看點

Unity程式設計筆錄--Unity Android 加密 so前言:正文:

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的圖

Unity程式設計筆錄--Unity Android 加密 so前言:正文:

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 打開。這裡的函數已經打不開了

借用兩張圖

Unity程式設計筆錄--Unity Android 加密 so前言:正文:
Unity程式設計筆錄--Unity Android 加密 so前言:正文: