吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6316|回复: 18
收起左侧

[Android 原创] 记一次逆向某大学app寻找签名算法

  [复制链接]
dazhige 发表于 2020-3-22 17:15

起因

在测试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就不放了,能改签名之后漏洞太多。。。

免费评分

参与人数 11吾爱币 +11 热心值 +11 收起 理由
loner. + 1 谢谢@Thanks!
hjsm + 1 + 1 热心回复!
我是湫 + 1 + 1 热心回复!
Anekys + 1 + 1 用心讨论,共获提升!
guo8122881 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
sky之城 + 1 + 1 我很赞同!
雨落惊鸿, + 1 + 1 用心讨论,共获提升!
涛之雨 + 2 + 1 用心讨论,共获提升!
camedeus + 1 + 1 我很赞同!
Lucifer_BW + 1 + 1 我很赞同!
三胖胖胖 + 1 + 1 热心回复!

查看全部评分

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

 楼主| dazhige 发表于 2020-3-22 21:06
yzlovew 发表于 2020-3-22 20:28
小白不太懂。就是找到签名有什么用处吗,是类似用户id那种吗?

这个app发送数据包的时候会带上签名来校验数据包是否被更改,我们通过找到签名的算法,来使我们可以自由更改数据包进行重放,进行漏洞挖掘等其他操作
 楼主| dazhige 发表于 2020-3-23 11:55
氓之嗤嗤 发表于 2020-3-22 22:08
这种情况app应该如何防御?

这个问题问得好,我个人觉得有很多角度:
第一是通过加壳、混淆等手段,让逆向过程变得复杂;
第二是保证后端尽量安全,就算攻击者能够改包重放,也找不到漏洞;
应该还有很多其他方法,我也只是略知一二,看看有没有大佬来解答下
zncliving 发表于 2020-3-22 17:34
大_鲸鱼 发表于 2020-3-22 18:50
收藏,谢谢啦
陈红h 发表于 2020-3-22 19:07
非常好  非常好  非常好  非常好
陈红h 发表于 2020-3-22 19:10
非常好  非常好  非常好  非常好
三胖胖胖 发表于 2020-3-22 19:11
感谢分享
prophet5 发表于 2020-3-22 19:16
学习了 还可以自己写sign方法
雨渊 发表于 2020-3-22 20:19
学习了学习了,原来还可以写sign
yzlovew 发表于 2020-3-22 20:28
小白不太懂。就是找到签名有什么用处吗,是类似用户id那种吗?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-24 20:53

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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