你的位置:首页 > 软件开发 > 操作系统 > 某android游戏逆向小记

某android游戏逆向小记

发布时间:2015-04-28 20:00:19
apktool 解包,jdgui查看jar发现代码非常少。查看代码发现它动态加载了一个he.jar。这个jar实际上是一个dex文件。这个dex文件用dex2jar反编译后发现里面代码仍然非常的少。重新打包后打manifest.  重新打包后发现一直出现签名校验错误。提示到官网下 ...

apktool 解包,jdgui查看jar发现代码非常少。查看代码发现它动态加载了一个he.jar。这个jar实际上是一个dex文件。这个dex文件用dex2jar反编译后发现里面代码仍然非常的少。

重新打包后打manifest.

  重新打包后发现一直出现签名校验错误。提示到官网下载apk。于是采用ida调试so文件。最重要的so文件是libhegame.so。最重要的东西都是在这个so里面。用ida在里面跟踪,被搞的很惨,因为绕来绕去总是在lua里面。度娘后才知道lua是一个脚本语言。原来在IDA里绕的都是lua的运行平台。在解压缩包里发现assets\src里面发现大量.lua加密过的文件。看来真正的游戏是运行在lua环境里面的。在IDA里调试的时候,一个偶然的机会发现一个重要的函数 load_lua。(其实也不是偶然,因为lua内容加载有几个函数,有从文件加载的,有从内存加载的,在从内存加载的地方断点,即可找到load_lua函数),这个函数是破解的关键。它用于加载所有加密的assets\src下面的所有lua加密文件。ida的f5查看C代码,再加上静态分析手段。基本上能看到它循环读取文件,并使用密钥解密lua密文的过程。它采用的是openssl的EVP_aes_128_cbc加密方式。密钥静态分析都能得到。再加上动态调试,可以拿到所有lua文件的明文。

#include "stdafx.h"#include "ZipUtils.h"#include "io.h"#include <stdio.h>#include <string.h>#include <errno.h>#include <winsock2.h>#include <Iphlpapi.h>#include <stdlib.h>#include <openssl/ssl.h>#include <openssl/err.h>#include <openssl/evp.h>#include <openssl/x509.h>#pragma comment( lib, "zlibwapi.lib")#define DATA_LEN 32#define EVP_MAX_KEY_LENGHT 64extern int ccInflateMemoryWithHint(unsigned char *in, unsigned int inLength, unsigned char **out, unsigned int *outLength, unsigned int outLenghtHint);//初始化密钥void keyInit(unsigned char* key){  int i = 0;  key[i++] = -23;  key[i++] = 116;  key[i++] = 125;  key[i++] = -110;  key[i++] = -52;  key[i++] = 50;  key[i++] = 46;  key[i++] = 125;  key[i++] = 17;  key[i++] = 46;  key[i++] = 124;  key[i++] = 52;  key[i++] = 81;  key[i++] = -41;  key[i++] = -77;  key[i++] = 106;}#define MAXLEN (1024 * 1024)int readFile(char * fileName, unsigned char ** buf){  FILE *fp;  int count;  if((fp=fopen(fileName,"rb"))==NULL){    printf("\nCannot open file strike any key exit!");    exit(1);  }  fseek(fp,0,SEEK_END); //定位到文件末   count = ftell(fp); //文件长度  fseek(fp,0,SEEK_SET);  (*buf) = (unsigned char*)malloc(count);  count = fread((*buf), sizeof(unsigned char), count, fp);  fclose(fp);  return count;}int writeFile(unsigned char * dest, int count, char * fileName){  FILE *fp;  if((fp=fopen(fileName,"wb"))==NULL){    exit(1);  }  count = fwrite((void *)dest, count, 1, fp);  fclose(fp);  return count;}int decryptAFile(char * path){  EVP_CIPHER_CTX ctx;  unsigned char key[16];  unsigned char out[1024] = {0};  int outl, tmp, i;  unsigned int len;  unsigned char * msg;  int count;  const EVP_CIPHER * type;  unsigned char * inBuf;  unsigned char * decode;  char outPath[256];  int rv;  keyInit(key);  count = readFile(path, &inBuf);  msg = inBuf + 16;  //OpenSSL_add_all_algorithms();  EVP_CIPHER_CTX_set_padding(&ctx, 0x07);  EVP_CIPHER_CTX_init(&ctx);  count -= 16;  unsigned char * dest = (unsigned char *)operator new[](count);  type = EVP_aes_128_cbc();  rv = EVP_DecryptInit_ex(&ctx, type, 0, key, inBuf);  if(rv != 1)  {      printf("Error");    return -1;  }    outl = 0;  rv = EVP_DecryptUpdate(&ctx, dest, &outl, msg, count);  if(rv != 1)  {      printf("Error");    EVP_CIPHER_CTX_cleanup(&ctx); //清除EVP加密上下文环境    return -1;  }  rv = EVP_DecryptFinal_ex(&ctx, dest + outl, &tmp);  outl = outl + tmp;    ccInflateMemoryWithHint(dest, outl, &decode, &len, 1024*1024);  EVP_CIPHER_CTX_cleanup(&ctx); //清除EVP加密上下文环境  strcpy(outPath, path);  path = strcat(outPath, ".c");  writeFile(decode, len, path);  printf("rn");  return 0;}/* * key:加密密钥 * iv:加密初始向量 * in_enc:明文数组,输入数组 * out_enc:加密后的数组,输出密文数组 * in_len:明文长度 * out_len:密文长度 * */unsigned char outBuffer[MaxLen];int EncryptBuffer(unsigned char * key,unsigned char *iv,unsigned char * in_enc, unsigned char ** out_enc,int in_len,int *out_len){  int outl; //第一次使用update加密的数据长度  int outl2; //剩余的字段,经过final填充后的长度  int inl;  int rv;  EVP_CIPHER_CTX ctx;    //OpenSSL_add_all_algorithms();  EVP_CIPHER_CTX_set_padding(&ctx, 0x07);  EVP_CIPHER_CTX_init(&ctx); //初始化ctx  rv = EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv); //设置密码算法、key和iv  if(rv != 1)  {    printf("Err\n");    return -1;  }  inl = in_len;  rv = EVP_EncryptUpdate(&ctx, (unsigned char *)outBuffer, &outl, in_enc, in_len);//加密  if(rv != 1)  {    printf("Err\n");    return -1;  }  //加密结束  rv = EVP_EncryptFinal_ex(&ctx, (unsigned char *)(outBuffer+outl), &outl2);  if(rv != 1)  {    EVP_CIPHER_CTX_cleanup(&ctx);    return -1;  }  *out_len=outl+outl2;  (*out_enc) = (unsigned char *) malloc(*out_len);  memset((*out_enc), 0, *out_len);  memcpy((void *)(*out_enc), (void *)outBuffer, *out_len);  EVP_CIPHER_CTX_cleanup(&ctx); //清除EVP加密上下文环境  printf("加密已完成\n");}/*launcher的ivvoid ivInit(unsigned char* iv){  int i = 0;  iv[i++] = 0xae;  iv[i++] = 0x4e;  iv[i++] = 0xec;  iv[i++] = 0x18;  iv[i++] = 0x1c;  iv[i++] = 0x95;  iv[i++] = 0x97;  iv[i++] = 0x3d;  iv[i++] = 0x0d;  iv[i++] = 0x40;  iv[i++] = 0xf5;  iv[i++] = 0xa2;  iv[i++] = 0x57;  iv[i++] = 0x34;  iv[i++] = 0x3d;  iv[i++] = 0x33;}*///LevelInfoPanel.a39d133180fc618a3a1964ae1f089c2a.luavoid ivInit(unsigned char* iv){  int i = 0;  iv[i++] = 0xb9;  iv[i++] = 0xec;  iv[i++] = 0x39;  iv[i++] = 0x96;  iv[i++] = 0x57;  iv[i++] = 0x69;  iv[i++] = 0x75;  iv[i++] = 0xbe;  iv[i++] = 0xa9;  iv[i++] = 0x9b;  iv[i++] = 0x93;  iv[i++] = 0x54;  iv[i++] = 0xcb;  iv[i++] = 0x6d;  iv[i++] = 0xf9;  iv[i++] = 0x17;}int gzcompress(unsigned char *data, int ndata,        unsigned char ** zdata, int*nzdata);void encryptAFile(char * path){  unsigned char * inFile;  unsigned char * zipBuf = NULL;  unsigned char * out_enc = NULL;  int zipLen;  char outPath[256];  int decode;  unsigned int len;  unsigned char * decodebuf;  int inLen, out_len;  unsigned char key[16];  unsigned char iv[16];  keyInit(key);  ivInit(iv);  inLen = readFile(path, &inFile);  gzcompress(inFile, inLen, (unsigned char ** )&zipBuf, &zipLen);  //ccInflateMemoryWithHint(zipBuf, zipLen, &decodebuf, &len, 1024*1024);  EncryptBuffer((unsigned char *)key, iv,(unsigned char *)zipBuf, &out_enc, zipLen, &out_len);  strcpy(outPath, path);  path = strcat(outPath, ".zp.out");  writeFile(out_enc, out_len, path);}const int PATH_MAXLEN = 1024;//定义最大目录长度unsigned long FILECOUNT = 0; //记录文件数量void ListDir(const char* pchData){  _finddata_t fdata; //定义文件查找结构对象  long done;  char tempdir[PATH_MAXLEN] = {0}; //定义一个临时字符数组  strcat(tempdir, pchData); //连接字符串  strcat(tempdir, "\\*"); //连接字符串(搜索以lua结尾的文件)  //cout << tempdir << endl;  done = _findfirst(tempdir, &fdata); //开始查找文件    if(done!=-1) //是否查找成功  {    int ret = 0;    while(ret!=-1) //定义一个循环    {      if(fdata.attrib != _A_SUBDIR) //判断文件属性      {        //cout << fdata.name << endl;        if(strcmp(fdata.name, "...")!=0 &&          strcmp(fdata.name, "..") != 0 &&          strcmp(fdata.name, ".") != 0) //过滤        {          char dir[PATH_MAXLEN] = {0}; //定义字符数组          strcat(dir, pchData); //连接字符串          strcat(dir, "\\");          strcat(dir, fdata.name);          if(strstr(dir, ".lua") && strstr(dir, ".out") == false)          {            decryptAFile(dir);          }          //cout << dir << endl; //输出查找到的文件名          FILECOUNT++; //累加文件个数        }      }      ret = _findnext(done, &fdata); //查找下一个文件      if(fdata.attrib == _A_SUBDIR && ret != -1) //判断文件属性,如果是目录,则递归调用      {        if(strcmp(fdata.name, "...") != 0 &&          strcmp(fdata.name, "..") != 0 &&           strcmp(fdata.name, ".") != 0) //过滤        {          char pdir[PATH_MAXLEN] = {0}; //定义字符数组          strcat(pdir, pchData); //连接字符串          strcat(pdir, "\\");          strcat(pdir, fdata.name);          //cout << pdir << endl; //输出要递归调用的目录名          ListDir(pdir); //递归调用        }      }    }  }}int main(){  char * path = "LevelInfoPanel.a39d133180fc618a3a1964ae1f089c2a.lua.c";  //ListDir(path);  encryptAFile(path);  return 0;}

原标题:某android游戏逆向小记

关键词:Android

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

可能感兴趣文章

我的浏览记录