记一次逆向某大学app寻找签名算法

https://www.52pojie.cn/thread-1137911-1-1.html

起因

在测试app的过程中,经常遇到有签名校验的情况,比如修改某个数据包中的参数,会出现签名不通过:
1.png
这个时候就得去找找这个sign参数,看看能不能由我们自己生成,从而修改参数进行重放。不然没法儿继续测了。

寻找

开始

老规矩,先查壳:
3.png
无壳,直接上逆向工具,我这里用的是apkide:

入口

从哪里入手呢?这里我用夜神安装好目标app后,随便点了几下,找到一个相对比较简单的数据包:
2.png
只有一个time和sign参数。
根据url特征,在apkide中搜索GetVersionInfo,定位到相关函数:
5.png
这里我们可以看到getVersionInfoCommand()调用了Lcom/hzsun/utility/Command;->creator,然后我们去找这个creator(下面我直接反编译成java代码,方便一些):
6.png
阅读这段代码,我们可以知道这个函数就是生成请求的参数的,paramArrayOfString1数组为参数的key,arrayOfString数组为参数的value。
这里只通过一个getTime()生成了第一个参数的value:

 复制代码 隐藏代码
arrayOfString[0] = getTime();

然后紧接着调用

 复制代码 隐藏代码
generateReq(paramArrayOfString1, arrayOfString)

来得到paramArrayOfString1,也就是我们的返回值,因此我们需要的sign值应该在generateReq()中寻找:
7.png
很明显,我们发现了这个getSignString()函数,看名字也就知道是生成sign的,继续跟进这个函数:
8.png
到这里我们就已经找到sign的生成方式了。

整理

 复制代码 隐藏代码
creator()-->generateReq(paramArrayOfString1, paramArrayOfString2)-->getSignString(paramArrayOfString1, paramArrayOfString2)

一路跟进下来:
paramArrayOfString1是参数的key;paramArrayOfString2是参数的value,也就是arrayOfString;
然后我们看看具体的处理方法:

 复制代码 隐藏代码
String[] arrayOfString = new String[paramArrayOfString1.length - 1];     for (int i = 0; i < paramArrayOfString1.length; i++)     {       if (i == 0) {         arrayOfString[i] = paramArrayOfString1[i];       }       if (i > 1) {         arrayOfString[(i - 1)] = paramArrayOfString1[i];       }     }

首先new一个新数组,比结果少一,为啥呢?因为要生成sign是根据其他的参数,所以少的这个一,就是sign。
然后:

 复制代码 隐藏代码
StringBuilder localStringBuilder = new StringBuilder();     int j = paramArrayOfString1.length;     for (i = 0; i < j; i++)     {       Object localObject = paramArrayOfString1[i];       for (int k = 0; k < paramArrayOfString1.length; k++) {         if (((String)localObject).equals(arrayOfString[k]))         {           localObject = new StringBuilder();           ((StringBuilder)localObject).append(paramArrayOfString2[k]);           ((StringBuilder)localObject).append("|");           localStringBuilder.append(((StringBuilder)localObject).toString());           break;         }       }     }

咋一看很复杂,其实仔细看,还是很有条理的。循环自然是读取参数的key-value,然后进行拼接,每一对中间用"|"来隔开。
看这个if:

 复制代码 隐藏代码
if (((String)localObject).equals(arrayOfString[k]))

比较的是啥呢?就是为了找出key对应的value。
循环拼接完然后再拼接一串:

 复制代码 隐藏代码
localStringBuilder.append("ok15we1@oid8x5afd@");

最后调用:

 复制代码 隐藏代码
Encrypt.Md5Encrypt(localStringBuilder.toString());

来得到sign,这个md5加密还是开发者自己写的。。。
继续跟进这个Md5Encrypt()函数:
9.png
这个不用看了,为啥呢?没必要,直接扒出来用就行了,哈哈哈。

结论

通过这一系列的跟进、读代码、整理,我们得到了这个sign的生成算法:
读取每个参数的value值,进行拼接,用"|"进行分隔,最后加上"ok15we1@oid8x5afd@",然后用开发者自己写的md5加密算法进行加密得到sign。
举个例子:
10.png
这里的参数是Time=20200322133104,
拼接出来:

20200322133104|ok15we1@oid8x5afd@

把加密算法扒出来用(直接copy的,忽略一下缩进):

 复制代码 隐藏代码
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.KeySpec; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESedeKeySpec; import javax.crypto.spec.IvParameterSpec; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Arrays; import java.util.Calendar; import java.util.Locale;

public class test {
    public static String getHexString(byte[] paramArrayOfByte)
  
{
    StringBuilder localStringBuilder = new StringBuilder();
    int i = paramArrayOfByte.length;
    for (int j = 0; j < i; j++)
    {
      String str = Integer.toHexString(paramArrayOfByte[j] & 0xFF);
      if (str.length() == 1) {
        localStringBuilder.append(‘0’);
      }
      localStringBuilder.append(str);
    }
    return localStringBuilder.toString();
  }
    public static String Md5Encrypt(String paramString)
  
{
    try
    {
      MessageDigest localMessageDigest = MessageDigest.getInstance(“MD5”);
      localMessageDigest.update(paramString.getBytes());
      paramString = getHexString(localMessageDigest.digest());
      return paramString;
    }
    catch (NoSuchAlgorithmException paramString1)
    {
      paramString1.printStackTrace();
    }
    return “”;
  }

    public static void main(String[] args) {
        String test = “20200322133104|ok15we1@oid8x5afd@”;

        System.out.println(Md5Encrypt(test));
    }
}

编译运行:
11.png
跟上面的burp比较一下,验证了我们的结果是对的

后面我验证了一下,其实就是一般的md5 32位加密方式:
12.png
是我多虑了。。。
至于多个参数,前面有个排序:

 复制代码 隐藏代码
Arrays.sort(paramArrayOfString1);

不赘诉了。
这个搞明白了,后续改包测试也就能进行了。
app就不放了,能改签名之后漏洞太多。。。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值