Evan422 发表于 2024-4-21 03:15

某练通登录sign算法逆向

本帖最后由 Evan422 于 2024-4-21 08:43 编辑

我们先抓一下包,然后准备apk逆向

一开始没有想到有壳的问题,拖入jadx分析后看目录类很少,凭少有的经验应该是加壳了,然后我们查一下壳

果然,百度的壳,但是后来用mt分析时发现是是伪百度,所以直接用黑盒进行脱壳,顺利脱掉,然后我们搜索UserLogin,
(其实最开始我搜的是sign,没有找到结果)

这里我们右键点击查找用例,只有一个结果,直接跟进去,来到login函数的定义处

这里其实我们就已经可以清晰的看到sign加密所需要的参数了,分别是{Action ,LoginID ,Pass ,OS,verifystr , HD,PhoneType,以及addFixedParams(params)所额外添加的参数),
根据抓包分析Action 的话是路由地址这里的话是UserLogin,
LoginID的话后来分析是请求UserTipForChangePass服务器返回的LoginID的值,
Pass的话是调用encryptionPs得到的加密值,这里是将用户密码以及请求UserTipForChangePass服务器返回的LoginID进行处理

我们自己实现一下这个加密逻辑

加密后与我们抓包得到的Pass一致,算法正确,这里贴一下代码
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class EncryptionHelper {

    public static String encryptionPs(String ps, String id) {
      String md5Ps = getMD5(ps);
      try {
            String en_psg = URLEncoder.encode(md5Ps + id, "GB2312");
            String unescape4 = java.net.URLDecoder.decode(en_psg, "GB2312");
            return getMD5(unescape4);
      } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return getMD5(md5Ps + id);
      }
    }

    public static String getMD5(String input) {
      try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] messageDigest = md.digest(input.getBytes());
            return convertByteToHex(messageDigest);
      } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
      }
    }

    private static String convertByteToHex(byte[] data) {
      StringBuilder sb = new StringBuilder();
      for (byte b : data) {
            sb.append(String.format("%02x", b));
      }
      return sb.toString();
    }

    public static void main(String[] args) {
      String ps = ""; //用户密码
      String id = ""; // 请求UserTipForChangePass服务器返回的LoginID值
      String encryptedPs = encryptionPs(ps, id);
      System.out.println("加密后的密码为: " + encryptedPs);
    }
}
OS,verifystr这两个参数可以固定,HD是随机生成的uuid值那么我们其实也可以固定

最后一个就是这个addFixedParams,我们看看它往我们的参数中添加了哪些额外参数,我们右键跟进去

其实也很清晰,这里面除了timestamp,剩下参数值都可以固定(UserID固定为零),这里的timestamp其实从长短能看出,是对当前时间戳经过处理后得到的,这里的话直接给出我实现后的时间戳处理代码
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.TimeZone;

public class TimeUtil {
    // 设定时间格式和时区
    private static final String FORMAT_DATE_TIME_STR = "yyyy-MM-dd HH:mm:ss";
    private static final TimeZone TIME_ZONE = TimeZone.getTimeZone("GMT+8");// GMT+8 时区

    // 获取当前时间的时间戳
    public static long getCurrentTimestamp() {
      Date currentTime = new Date();
      SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_DATE_TIME_STR);
      formatter.setTimeZone(TIME_ZONE); // 设置时区
      String formattedDate = formatter.format(currentTime);

      try {
            Date parsedDate = formatter.parse(formattedDate);
            return parsedDate.getTime() / 1000; // 转换为秒
      } catch (Exception e) {
            e.printStackTrace();
            return 0L; // 出现错误时返回0
      }
    }
}

对了,还有一个密钥,这个的话很简单,一直点跳转到声明就跟进去了


最后的话我们实现以下sign的代码进行测试
private static String getSign(String data) {
      try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hashInBytes = md.digest(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : hashInBytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
      } catch (NoSuchAlgorithmException e) {
            System.err.println("MD5 Algorithm not found: " + e.getMessage());
            return null;
      }
    }

登录成功,sign算法正确,最后附上完整测试代码
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.stream.Stream;

public class Demo {
    private static final String SIGN_KEY = "9c7b9399680658d308691f2acad58c0a";
    private static final String BASE_URL = "https://server.dailiantong.com.cn/API/APPService.ashx";

    public static void main(String[] args) {
      String timestamp = getCurrentTimestamp();
      String paramsValueStr = Stream.of(
                        "UserLogin", "UserTipForChangePass返回的LoginID值", "pass的加密值", "Android", "", "bdcba826-8ff0-4b5d-9428-7424a13c51fb",
                        "HUAWEI CAZ-AL10", "0", timestamp, "1.0", "Android", "DLTAndroid", "4.8.7", "huawei")
                .reduce("", String::concat);

      String fullStr = SIGN_KEY + paramsValueStr;
      String calculatedSign = getSign(fullStr);
      System.out.println("Sign: " + calculatedSign);

      try {
            String query = "?Action=UserLogin&LoginID=LoginID值&Pass=pss加密值&OS=Android&verifystr=&HD=bdcba826-8ff0-4b5d-9428-7424a13c51fb&PhoneType=HUAWEI%20CAZ-AL10&UserID=0&TimeStamp=" + timestamp + "&Ver=1.0&AppOS=Android&AppID=DLTAndroid&AppVer=4.8.7&ODM=huawei&Sign=" + calculatedSign;
            URL url = new URL(BASE_URL + query);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");

            int responseCode = conn.getResponseCode();
            System.out.println("Response Code : " + responseCode);
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String inputLine;
            StringBuilder response = new StringBuilder();
            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();
            System.out.println("Response Body : " + response.toString());
      } catch (Exception e) {
            System.err.println("Error during HTTP request: " + e.getMessage());
      }
    }

    private static String getSign(String data) {
      try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hashInBytes = md.digest(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : hashInBytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
      } catch (NoSuchAlgorithmException e) {
            System.err.println("MD5 Algorithm not found: " + e.getMessage());
            return null;
      }
    }

    public static String getCurrentTimestamp() {
      Date currentTime = new Date();
      SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      formatter.setTimeZone(TimeZone.getTimeZone("GMT+8"));
      String formattedDate = formatter.format(currentTime);

      try {
            Date parsedDate = formatter.parse(formattedDate);
            String date = String.valueOf(parsedDate.getTime() / 1000);
            return date; // 转换为秒
      } catch (Exception e) {
            e.printStackTrace();
            return "0";
      }
    }
}

逆向小白,大佬勿喷感谢

Evan422 发表于 2024-4-21 08:55

图掉了,重新贴一下

正己 发表于 2024-4-21 10:06

有部分代码块是割裂,你可以再完善一下

Evan422 发表于 2024-4-21 10:11

正己 发表于 2024-4-21 10:06
有部分代码块是割裂,你可以再完善一下

好的好的

hualy 发表于 2024-4-21 11:16


来学习一下

clovert 发表于 2024-4-21 11:34

看看再说

zyastc521 发表于 2024-4-21 12:45

已收藏,感谢分享!

wyp123 发表于 2024-4-21 13:56

黑盒进行脱壳的黑盒是啥

qwq23496 发表于 2024-4-21 14:19

来学习学习

Evan422 发表于 2024-4-21 14:47

wyp123 发表于 2024-4-21 13:56
黑盒进行脱壳的黑盒是啥

BlackBox32位
页: [1] 2
查看完整版本: 某练通登录sign算法逆向