吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6814|回复: 25
收起左侧

[Android 原创] APP网络请求参数校验,逆向分析分享

  [复制链接]
wxyzyou 发表于 2020-3-22 22:16
本帖最后由 wxyzyou 于 2020-3-22 22:24 编辑

分享一个APP post参数验签校验案例
仅分享逆向分析过程,关键代码都有,还有截图,就不发APP了

APP中有个实名认证功能,需要上传身份证正反和身份证信息
提交POST时,有个keystr参数,是用来校验其它参数信息的,分析目标就是它了

Fiddler抓到的包文,keystr参数看起来像是一个MD5

post.jpg

用的ApkToolAid,对APP进行查壳,反编译
查到结果,未加壳,反编译正常,但是分析代码和文件,发现它是 ReactNative+NodeJS  开发的APP
此种类型的APP大都是把整个项目编译成一个 index.android.bundle 文件,放置于安卓目录结构的 assets路径下

把index.android.bundle文件,后缀名改为js, index.android.js 用相关JS编辑工具打开该文件,进行分析(我用的code)

chake.jpg
apk1.jpg
apk2.jpg
用Code格式化JS代码,方便查看,直接在JS文件中搜索关键字,POST参数里的几个参数名,定位到相关函数 uploadPersonalInfo (有几个类似的搜索结果,经过对比参数,确定函数位置)
从代码上看keystr是通过参数 t 传递进来的,搜索 uploadPersonalInfo函数名,找到调用位置,发现参数时来自全局对象的 c.NativeModules.HeHe.getKey
再次在JS文件中搜索getKey函数,未找到,那就只有1个可能,该函数在android代码里
我们先确认好,getKey传递的几个参数内容
参数内容,pid,   fPhone手机号,   IDCard身份证号,   Name姓名
getKey(this.props.member.pId, this.props.member.fPhone, this.props.IDCard, this.props.Name)


up.jpg

up1.jpg


用ApkToolAid再次生成app的jar包,方便我们使用 jd-gui 工具,查看JAVA明文代码 (反编译的文件是dex类型,需要再次转为jar查看)
在jar包里搜索全局对象里的关键字 HeHe或getKey 定位到目标函数,对比参数数量和类型,确认无误
跟进c.a(paramInteger, paramString1, paramString2, paramString3) 函数体内
可以看到加密方式就在眼前了.(图片和代码在下面)

对代码进行分析时卡在函数内的第一行 new a().a(paramString3),
我们知道参数 paramString3是姓名,这 new a().a 对姓名做了什么处理,这里卡了我比较久

因为反编译的代码并非完全正确,对逆向分析和代码测试都带来了不少困扰
经过代码修复和测试,最后在百度搜索,修复代码生成的 private int[] c = new int[27] 数组内的内容时,无意中发现别人的JAVA生成姓名的首字母代码案例,内容比较一致,就大胆认为,它就是获取姓名首字母
(后经算法生成结果对比,确认了确实是姓名首字母)

此图就是JAVA获取姓名首字母的算法用到的数组

jar3.jpg

通过对加密函数的分析,发现大量的字符串拼接和MD5调用.
详细分析结果,已经注释到了代码中,转至下方代码查看



易语言加密算法函数
(如易语言引起不适,多多包涵)
e.jpg


下图为加密函数

jar1.jpg
jar2.jpg

关键代码,加入了分析结果和注释

[Java] 纯文本查看 复制代码
package com.GeneApp.invokenative;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class c
{
  private static final String[] a = { "A", "c", "9", "G", "H", "5", "P", "v", "7", "m", "d" };//密码本a
  private static final String[] b = { "l", "I", "1", "O", "o", "w", "u", "#", "n", "q", "E" };//密码本b
  private static final String c = "pjjsdsb";
  
  public static String a(Integer paramInteger, String paramString1, String paramString2, String paramString3)
  {
//获取姓名的首字母    
paramString3 = new a().a(paramString3);
//获取首字母的第一位,姓的字母
    String str = paramString3.substring(0, 1);

//获取手机号的第3 6 8位的内容转为整数型,从上方的 b 数组中获取对应 3 6 8的成员,拼接为字符串,赋值给 localObject1
    Object localObject1 = new StringBuilder();
    ((StringBuilder)localObject1).append(b[Integer.parseInt(paramString1.substring(2, 3))]);
    ((StringBuilder)localObject1).append(b[Integer.parseInt(paramString1.substring(5, 6))]);
    ((StringBuilder)localObject1).append(b[Integer.parseInt(paramString1.substring(7, 8))]);
    localObject1 = ((StringBuilder)localObject1).toString();
//拼接字符串 _和参数pid+6的结果 赋值给 localObject2
//如果pid为 384187  拼接结果为  _384193
    Object localObject2 = new StringBuilder();
    ((StringBuilder)localObject2).append("_");
    ((StringBuilder)localObject2).append(paramInteger.intValue() + 6);
    paramInteger = "";
    ((StringBuilder)localObject2).append("");
    localObject2 = ((StringBuilder)localObject2).toString();
//获取手机号中的第4 5 7的内容转为整数型,从上方的 a 数组中获取对应 4 5 7的成员,拼接为字符串,赋值给 paramString1 
    Object localObject3 = new StringBuilder();
    ((StringBuilder)localObject3).append(a[Integer.parseInt(paramString1.substring(3, 4))]);
    ((StringBuilder)localObject3).append(a[Integer.parseInt(paramString1.substring(4, 5))]);
    ((StringBuilder)localObject3).append(a[Integer.parseInt(paramString1.substring(6, 7))]);
    paramString1 = ((StringBuilder)localObject3).toString();

//身份证的拼接
    localObject3 = new StringBuilder();
//获取身份证号的,第4位开始的3个字符的内容 拼接
    ((StringBuilder)localObject3).append(paramString2.substring(3, 6));
//获取身份证号的,第13位开始的4个字符的内容 拼接
    ((StringBuilder)localObject3).append(paramString2.substring(12, 16));
//拼接一个#
    ((StringBuilder)localObject3).append("#");
//获取身份证号的,第10位开始的5个字符的内容 拼接
    ((StringBuilder)localObject3).append(paramString2.substring(9, 14));
//拼接结果案例 7841174#01011
//拼接结果,赋值给 localObject3
    localObject3 = ((StringBuilder)localObject3).toString();
/
/把以上所有拼接结果再次拼接
//拼接结果案例 Z#IO_3841937cH7841174#01011ZBJ
    paramString2 = new StringBuilder();
//拼接姓首字母
    paramString2.append(str);
//拼接第1个结果
    paramString2.append((String)localObject1);
//拼接第2个结果
    paramString2.append((String)localObject2);
//拼接第3个结果
    paramString2.append(paramString1);
//拼接第4个结果
    paramString2.append((String)localObject3);
//拼接姓名首字母
    paramString2.append(paramString3);
//拼接结果,赋值给 paramString1
    paramString1 = paramString2.toString();
    try
    {
//最终拼接
      paramString2 = new java/lang/StringBuilder;
      paramString2.<init>();
//把上面的拼接结果,生成MD5 拼接
      paramString2.append(a(paramString1));
//拼接 pjjsdsb
      paramString2.append("pjjsdsb");
//拼接案例 ff69dd766a0be2705c5dff6ddf71d96fpjjsdsb
//把最终拼接的结果,生成MD5返回
      paramString1 = a(paramString2.toString());
      paramInteger = paramString1;
    }
    catch (Exception paramString1)
    {
      for (;;) {}
    }
    return paramInteger;
  }
  //生成MD5函数
  public static String a(String paramString)
    throws Exception
  {
    try
    {
      paramString = MessageDigest.getInstance("MD5").digest(paramString.getBytes("UTF-8"));
      StringBuilder localStringBuilder = new StringBuilder(paramString.length * 2);
      int i = paramString.length;
      for (int j = 0; j < i; j++)
      {
        int k = paramString[j] & 0xFF;
        if (k < 16) {
          localStringBuilder.append("0");
        }
        localStringBuilder.append(Integer.toHexString(k));
      }
      return localStringBuilder.toString();
    }
    catch (UnsupportedEncodingException paramString)
    {
      throw new RuntimeException("Huh, UTF-8 should be supported?", paramString);
    }
    catch (NoSuchAlgorithmException paramString)
    {
      paramString = new RuntimeException("Huh, MD5 should be supported?", paramString);
    }
    for (;;)
    {
      throw paramString;
    }
  }
}



获取姓名首字母的类


[Java] 纯文本查看 复制代码
package com.GeneApp.invokenative;

import java.io.PrintStream;

public class a
{
  private char[] a = { 21834, -32083, 25830, 25645, -30978, 21457, 22134, 21704, 21704, 20987, 21888, 22403, 22920, 25343, 21734, 21866, 26399, 28982, 25746, 22604, 22604, 22604, 25366, 26132, 21387, 21277, 24231 };
  private char[] b = { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90 };
  private int[] c = new int[27];
  
  public a()
  {
    for (int i = 0; i < 27; i++) {
      this.c[i] = b(this.a[i]);
    }
  }
  
  public static void a(String[] paramArrayOfString)
  {
    paramArrayOfString = new a();
    System.out.println(paramArrayOfString.a("是"));
  }
  
  private boolean a(int paramInt1, int paramInt2)
  {
    if (paramInt2 < this.c[paramInt1]) {
      return false;
    }
    for (int i = paramInt1 + 1; i < 26; i++)
    {
      int[] arrayOfInt = this.c;
      if (arrayOfInt[i] != arrayOfInt[paramInt1]) {
        break;
      }
    }
    boolean bool1 = true;
    boolean bool2 = true;
    if (i == 26)
    {
      if (paramInt2 > this.c[i]) {
        bool2 = false;
      }
      return bool2;
    }
    if (paramInt2 < this.c[i]) {
      bool2 = bool1;
    } else {
      bool2 = false;
    }
    return bool2;
  }
  
  private int b(char paramChar)
  {
    Object localObject = new String();
    StringBuilder localStringBuilder = new StringBuilder();
    localStringBuilder.append((String)localObject);
    localStringBuilder.append(paramChar);
    localObject = localStringBuilder.toString();
    try
    {
      localObject = ((String)localObject).getBytes("GB2312");
      if (localObject.length < 2) {
        return 0;
      }
      int i = localObject[0];
      int j = localObject[1];
      return (i << 8 & 0xFF00) + (j & 0xFF);
    }
    catch (Exception localException) {}
    return 0;
  }
  
  public char a(char paramChar)
  {
    if ((paramChar >= 'a') && (paramChar <= 'z')) {
      return (char)(paramChar - 'a' + 65);
    }
    if ((paramChar >= 'A') && (paramChar <= 'Z')) {
      return paramChar;
    }
    int i = b(paramChar);
    int[] arrayOfInt = this.c;
    int j = 0;
    if (i < arrayOfInt[0]) {
      return '0';
    }
    while ((j < 26) && (!a(j, i))) {
      j++;
    }
    if (j >= 26) {
      return '0';
    }
    return this.b[j];
  }
  
  public String a(String paramString)
  {
    int i = paramString.length();
    int j = 0;
    str1 = "";
    str2 = "";
    for (;;)
    {
      if (j < i) {}
      try
      {
        StringBuilder localStringBuilder = new java/lang/StringBuilder;
        localStringBuilder.<init>();
        localStringBuilder.append(str2);
        localStringBuilder.append(a(paramString.charAt(j)));
        str2 = localStringBuilder.toString();
        j++;
      }
      catch (Exception paramString)
      {
        for (;;)
        {
          str2 = str1;
        }
      }
    }
    return str2;
  }
}






免费评分

参与人数 17吾爱币 +11 热心值 +16 收起 理由
a1275639564 + 1 + 1 谢谢@Thanks!
miner123 + 1 谢谢@Thanks!
NB2665597272 + 1 + 1 我很赞同!
zx7206 + 1 谢谢@Thanks!
fushanpupil + 1 我很赞同!
暗夜杀神 + 1 + 1 谢谢@Thanks!
UPC + 1 + 1 我很赞同!
zhoumeto + 1 + 1 用心讨论,共获提升!
fuhaokj + 1 + 1 膜拜大神
zhp_king + 1 谢谢@Thanks!
小小莱 + 1 + 1 谢谢@Thanks!
mifeng + 1 + 1 我很赞同!
爱吾爱 + 1 + 1 我很赞同!
a66756675 + 1 + 1 谢谢@Thanks!
楠西 + 1 谢谢@Thanks!
东方学痞 + 1 我很赞同!
ZainFelice + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| wxyzyou 发表于 2020-3-24 13:53
非凡公子 发表于 2020-3-24 11:29
楼主可否分享一下你那个安卓查壳软件

52的爱盘里,安卓工具
看,六眼飞鱼 发表于 2020-3-22 22:30
52Douyin 发表于 2020-3-22 22:35
sddson 发表于 2020-3-22 23:49
谢谢分享
bjxiaoyao 发表于 2020-3-23 06:26
学习了分析很专业
Parkourr 发表于 2020-3-23 07:58
这么强的吗?感谢分享
东方学痞 发表于 2020-3-23 08:08
来学习学习
liangdaofeng 发表于 2020-3-23 08:24
感谢分享
tvrcfdfe 发表于 2020-3-23 08:55
这个厉害了  666666感谢
15295828305 发表于 2020-3-23 08:59
我也要学会
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-26 07:07

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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