killer5 发表于 2016-6-2 18:56

对某APP的逆向之旅(1)

本帖最后由 killer5 于 2016-6-2 18:58 编辑

最近在研究游戏辅助相关的技术,因此有了下面的文章,仅供娱乐。
前期的简单分析工作,简单略过,直接进入主题。
java层代码如下:
static {
      System.loadLibrary("encode");
    }

    public static int a(Context arg4) {
      if(Thread.currentThread().getName().equals("main")) {
            throw new IllegalStateException("runRootCommand can\'t run on main thread.");
      }

      int v0 = -1;
      try {
            String v1 = String.valueOf(arg4.getFilesDir().getAbsolutePath()) + "/gearcorez";
            String v2 = System.getProperty("os.arch");
            if(v2 != null && !v2.contains("arm")) {
                return v0;
            }

            RootManager.a(arg4, "gearcore", v1);
            RootManager.a(arg4, "speedman", arg4.getFilesDir() + "/libspeedman.so");
            String v0_2 = String.valueOf(arg4.getFilesDir().getAbsolutePath()) + "/gearcore";
            RootManager.decode(arg4.getApplicationContext(), v1, v0_2);
            v0 = RootManager.a(arg4.getApplicationContext(), v0_2);
            ag.a("suc=" + v0);
      }
      catch(IOException v0_1) {
            IOException v1_1 = v0_1;
            v0 = -2;
            v1_1.printStackTrace();
      }

      arg4.deleteFile("gearcore");
      return v0;
    }

可以看到加载了encode动态库,并在函数a(Context arg4)中进行处理。过程为首先通过RootManager.a(, ,)函数进行文件拷贝,然后通过RootManager.decode函数进行解密,最后通过函数RootManager.a(,)运行解密后的文件。其中函数RootManager.a(, ,)如下:
private static void a(Context arg5, String arg6, String arg7) {
      int v3;
      BufferedInputStream v1 = new BufferedInputStream(arg5.getAssets().open(arg6));
      FileOutputStream v2 = new FileOutputStream(arg7);
      byte[] v0 = new byte;
      try {
            while(true) {
            label_6:
                v3 = v1.read(v0);
                if(v3 != -1) {
                  goto label_14;
                }

                v2.flush();
                break;
            }
      }
      catch(Throwable v0_1) {
            goto label_17;
      }

      v2.close();
      v1.close();
      return;
      try {
      label_14:
            v2.write(v0, 0, v3);
            goto label_6;
      }
      catch(Throwable v0_1) {
      label_17:
            v2.close();
            v1.close();
            throw v0_1;
      }
    }


将assets中文件拷贝到指定目录中,函数比较简单。本节,我们主要来分析下解密函数IDA载入encode动态库文件,来到decode函数处



F5,其中参数名和变量名,是我修改过的,这个根据个人习惯,分析时修改即可。





获取包名





然后,通过算法解密字符串,算法如下:



然后,将解密后的字符传和包名比较,根据比较结果,进行文件的解密操作。



最后,附上算法还原后的代码(我也写到了so文件中),如下:
static unsigned char key = { 0x70,0,0xc8,0,0x74,0,0x6a,0,0x32,0,
                                 0x78,0,0x68,0,0x7b,0,0x77,0,0x78,0,
                                 0x76,0,0x7e,0,0x3a,0,0x74,0,0x6f,0,
                                 0x7c,0,0x75,0,0x84,0,0x82,0,0x78,0,
                                 0x79,0,0x79,0,0x16,0,0};

JNIEXPORT void JNICALL Java_example_com_applications_MainActivity_decode(JNIEnv *env, jobject obj, jobject context, jstring encodePath, jstring decodePath)
{
    char temp;
    char decodeKey;
    //Context的类
    jclass context_clazz = env->GetObjectClass(context);
    // 得到 getPackageName 方法的 ID
    jmethodID methodId_package = env->GetMethodID(context_clazz, "getPackageName", "()Ljava/lang/String;");
    if (methodId_package != NULL)
    {
      // 获得当前应用的包名
      jstring appPackageName = (jstring)env->CallObjectMethod(context, methodId_package);
      const char* strPackage = env->GetStringUTFChars(appPackageName, 0);
      LOGD("package name: %s", strPackage);
      memcpy(temp, key, 0x2e);

      int value = -1, index = 0, salt = 0;
      for (int i = 0, j = 1; j < 23; ++i, ++j, ++value)
      {
            salt = 0;
            index = i;
            if (i > 0)
            {
                if (i == 1)
                  continue;
                index = value;
                salt = value;
            }

            decodeKey = temp - 1 - salt;
      }

      char* pMem = NULL;
      pMem = (char*)malloc(0x16);
      snprintf(pMem, 0x16, "%s", decodeKey);
      LOGD("decodeKey: %s", decodeKey);

      //简单的保护工作
      if (!strcmp(strPackage, pMem))
      {
            const char* strEncodePath = env->GetStringUTFChars(encodePath, 0);
            const char* strDecodePath = env->GetStringUTFChars(decodePath, 0);
            FILE *fpEncode = fopen(strEncodePath, "rb");
            if (fpEncode)
            {
                FILE *fpDecode = fopen(strDecodePath, "wb");
                if (fpDecode)
                {
                  fseek(fpEncode, 0, 0);
                  uint8_t nextByte;
                  fread(&nextByte, 1, 1, fpEncode);
                  uint8_t xorKey = nextByte;
                  uint8_t decodeByte;

                  while (fread(&nextByte, 1, 1, fpEncode))
                  {
                        decodeByte = ~((nextByte - 30) ^ xorKey) & 0xFF;
                        nextByte = decodeByte;
                        fputc(decodeByte, fpDecode);
                  }

                  fclose(fpDecode);
                  chmod(strDecodePath, 0x1ED);
                }
                fclose(fpEncode);
            }
            remove(strEncodePath);
            env->ReleaseStringUTFChars(encodePath, strEncodePath);
            env->ReleaseStringUTFChars(decodePath, strDecodePath);
            env->ReleaseStringUTFChars(appPackageName, strPackage);
            free(pMem);
      }
      else
      {
            env->ReleaseStringUTFChars(appPackageName, strPackage);
            free(pMem);
      }
    }
}


其中上面解密后的字符串为



分析过程比较简单,仅供娱乐!至于解密后的文件,大家可以自行分析。



saliey 发表于 2016-6-3 13:11

正在搞一个IM文字消息发送的加密方式,找到消息发送出去的点了,但还是没有找到是加密的代码,加密会在哪

回归自然 发表于 2016-6-2 20:18

专业抢沙发二十年···

叼烟的声音 发表于 2016-6-2 20:24

这个顶一下

qtfreet00 发表于 2016-6-3 09:21

最好把附件也传上来

saliey 发表于 2016-6-3 13:06

SHOUAJIAOLE

Aill 发表于 2016-6-3 17:12

KUKUZHU1987 发表于 2016-6-13 14:44

谢谢分享

mmmmar 发表于 2016-6-13 15:38

厉害,so层逆向现在还看不到

Gordon0918 发表于 2016-6-15 09:42

佩服,可以看伪代码还原原来代码,功力很深啊
页: [1] 2
查看完整版本: 对某APP的逆向之旅(1)