发新帖

逆向Android SO,以《遇见》App为例

[复制链接]
13685 7
本帖最后由 水波摇曳 于 2015-4-22 18:44 编辑

工具:IDA6.6
不会发图,只能用文字描述了。

研究过《遇见》登录的同学可能都知道,只要修改过APK,在登录时都会提示软件盗版。
经研究登录时safecode字段有问题,可能是验证了签名。
在libiaroundnet.so中Java_net_iaround_utils_NativeLibUtil_aaa实现。

步骤1:
下载  http://gdown.baidu.com/data/wisegame...yujian_570.apk将yujian_570.apk改名为yujian_570.zip,将lib目录下的lib\armeabi\libiaroundnet.so提取出来拖入IDA中。

步骤2:
拖进来后发现SHA1Input,SHA1Output这样带有SHA1的函数,断定SO中用到了SHA1加密算法。

步骤3:

查看Java_net_iaround_utils_NativeLibUtil_aaa函数按F5出来C的伪代码,此时代码还不易读懂,不如像“v9 = (*(int (**)(void))(v8 + 124))()”,这样的代码。但是看到这个我们是不是应该想到C++里面的内存的寻址?,成员变量的首地址=对象的首地址+成员变量在类中的偏移.
假如
[C] 纯文本查看 复制代码
Struct node
{
      int,x,y;
};

node a;如果A的首地址是0x00000000,那么a.x的地址是0x00000000,a.y的地址是0x00000004;
由C伪代码我们不难得出:v8是JNIEnv *类型,查看jni.h并计算我们不难发现v8 + 124是GetObjectClass函数指针,同理我们可以计算出其他类似的调用。

步骤4:
有了步骤三的基础,我们可以想到一个简便的方法就是导入jni.h,从而确定JNIEnv类的结构,借助IDA直接识别函数。
下载jni.h(虫大大书上修改JNI.H的方法貌似不好使,我自己改了个):http://pan.baidu.com/s/1sj3EaTn,
然后File->Load File->parse C header file... 选择下载好的jni.h确定。
然后在structures页面里按下insert键
->add standard structure->滑到最下方选择"JNIEnv_",然后确定。

步骤5:
返回伪代码界面,鼠标点击Java_net_iaround_utils_NativeLibUtil_aaa的第一个参数,
右键->convert to struct*->选择jnienv_*,这时,伪代码中不明函数的调用已经出来。
刚刚的(*(int (**)(void))(v8 + 124))()已经变成 ((int (*)(void))v8->GetObjectClass)();
这时只要仔细阅读伪代码就知道该函数做了啥,逆向的C代码为:
这时代码的意图就明显了
[C] 纯文本查看 复制代码
JNIEXPORT jstring JNICALL Java_net_iaround_utils_NativeLibUtil_aaa
(JNIEnv *env, jobject ob, jobject paramContext, jobject paramPackageInfo, jstring paramString1, jstring paramString2)
{
  SHA1Context context;
  jstring ret=0;
  JNIEnv* p_env=env;
  char cifferbuf[128]={0};
  char buf[64]={0};
  jclass obClass=env->GetObjectClass(ob);
  char *pString="()Ljava/lang/String;";
  jmethodID  reusltID=p_env->GetMethodID(obClass,"getPackageName","()Ljava/lang/String;");
  jobject retObject=p_env->CallObjectMethod(paramContext,reusltID);
  jboolean boolean=0;
  const char *str=p_env->GetStringUTFChars((jstring)retObject,&boolean);
  bool cmpResult=strcmp("net.iaround",str);
  if(cmpResult==0)
  {
    ret=p_env->NewStringUTF("123456");
  }
  else
  {
    char destBuffer[128]={0};
    const char* s1=p_env->GetStringUTFChars(paramString1,&boolean);
    const char *s2=p_env->GetStringUTFChars(paramString2,&boolean);
    __android_log_print(4, "logfromc", "%s",s1);
    __android_log_print(4, "logfromc", "%s",s2);
    int len1=strlen(s1);
    int len2=strlen(s2);
    memcpy(destBuffer,s1,len1);
    memcpy(destBuffer+len1,s2,len2);
    jclass packageInfo= p_env->GetObjectClass(paramPackageInfo);
    ret=(jstring)packageInfo;
    if(packageInfo)
    {
      jfieldID signatures=p_env->GetFieldID(packageInfo,"signatures","[Landroid/content/pm/Signature;");
      ret=(jstring)signatures;
      if(signatures)
      {

        jobjectArray sigarray=(jobjectArray)p_env->GetObjectField(paramPackageInfo,signatures);
        if(sigarray)
        {

          jsize sigLength=p_env->GetArrayLength(sigarray);
          if(sigLength>0)
          {

            jobject ob=p_env->GetObjectArrayElement(sigarray,0);
            if(ob)
            {

              jclass jc=p_env->GetObjectClass(ob);
              if(jc)
              {

                jmethodID id=p_env->GetMethodID(jc,"toCharsString","()Ljava/lang/String;");
                if(id)
                {

                  jstring sigString =(jstring)p_env->CallObjectMethod(ob,id);
                  const char* sigStr=p_env->GetStringUTFChars(sigString,&boolean);
                  int passLen=strlen(destBuffer);
                  memcpy(cifferbuf,sigStr+16,32);
                  //__android_log_print(4, "logfromc", "%s",sigStr+16);
                  //memcpy(cifferbuf,"a00302010202044e549d3a300d06092a",32);
                  memcpy(cifferbuf+32,destBuffer,passLen);

                  __android_log_print(4, "logfromc", "cifferString:%s",cifferbuf);

                  SHA1Reset(&context);
                  int cifferlen=strlen(cifferbuf);

                  SHA1Input(&context,cifferbuf,cifferlen);
                  //memset(&v51, 48, 0x29u);
                  memset(buf,0,sizeof(buf));
                  if(SHA1Result(&context))
                  {
                    sprintf(buf, "%08X%08X%08X%08X%08X", context.Message_Digest[0],context.Message_Digest[1],
                        context.Message_Digest[2],context.Message_Digest[3],context.Message_Digest[4]);
                  }
                  else
                  {
                    __android_log_print(4, "logfromc", "ERROR-- could not compute message digest\n");
                  }
                  ret=p_env->NewStringUTF(buf);
                }
              }
            }
          }
        }
      }
    }
  }
  return ret;
}

一句话概括就是:将签名中取出32位+paramString1+paramString2后用SHA1加密
                        如果中途出错,直接返回“123456”。。。

这时可以得出该APK包是利用了签名加密后到服务器验证的。服务器计算出safecode和收到的safecode不一样时,反馈软件盗版。。

此贴只供学习,如做其它用途,后果自负。      


                                                               

举报 使用道具

回复

精彩评论7

cvt126    发表于 2015-4-22 18:50:04 | 显示全部楼层
板凳?看看 支持

举报 使用道具

回复 支持 反对
单翅的天使ylj    发表于 2015-4-22 22:20:43 | 显示全部楼层
看不懂哈哈

举报 使用道具

回复 支持 反对
大兵beyond    发表于 2015-4-23 13:49:11 | 显示全部楼层

举报 使用道具

回复
荆轲刺秦琼    发表于 2015-4-24 13:41:00 | 显示全部楼层
  狂顶  

举报 使用道具

回复 支持 反对
晨晨    发表于 2015-7-22 09:58:26 | 显示全部楼层
我想问一下。。你转载了这篇文章。。是操作过还是单纯为技术而转载?

举报 使用道具

回复 支持 反对
美丽中带着刺    发表于 2015-7-31 15:27:16 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

举报 使用道具

回复 支持 反对
飘飘然的感觉    发表于 2015-7-31 16:17:21 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

举报 使用道具

回复 支持 反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表