某练通登录sign算法逆向

查看 97|回复 9
作者:Evan422   
我们先抓一下包,然后准备apk逆向


91fee738c95dcec9f53047d4ad45944.png (65.71 KB, 下载次数: 0)
下载附件
2024-4-21 08:30 上传

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


7fc146687c870e80182486af505e05c.jpg (350.88 KB, 下载次数: 0)
下载附件
2024-4-21 08:30 上传

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


b3f173aa9f86bac765954e37c2eab60.png (31.17 KB, 下载次数: 0)
下载附件
2024-4-21 08:32 上传

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


b311c06816fcf90fdab374b7761fbca.png (40.19 KB, 下载次数: 0)
下载附件
2024-4-21 08:33 上传

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


b3f173aa9f86bac765954e37c2eab60.png (31.17 KB, 下载次数: 0)
下载附件
2024-4-21 08:34 上传

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


7eecd3c01a7e84fd9b592e297b6f015.png (117.3 KB, 下载次数: 0)
下载附件
2024-4-21 08:36 上传

加密后与我们抓包得到的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值那么我们其实也可以固定


79c2f9e7bc2bc06a55235b67f45a7af.png (9 KB, 下载次数: 0)
下载附件
2024-4-21 08:37 上传

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


f028e0e2f373ea60a9a4fe26dd635cc.png (30.28 KB, 下载次数: 0)
下载附件
2024-4-21 08:40 上传

其实也很清晰,这里面除了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
    }
}
}
对了,还有一个密钥,这个的话很简单,一直点跳转到声明就跟进去了


55c729a4491075c16965abd85d2c4cd.png (19.35 KB, 下载次数: 0)
下载附件
2024-4-21 08:42 上传

最后的话我们实现以下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;
}
}


12758148a648c5ea22f2309c9f0cac0.png (153.59 KB, 下载次数: 0)
下载附件
2024-4-21 08:43 上传

登录成功,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
OP
  

图掉了,重新贴一下
Evan422
OP
  

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


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

好的好的
clovert   

来学习一下
zyastc521   

看看再说
wyp123   

已收藏,感谢分享!
qwq23496   

黑盒进行脱壳的黑盒是啥
Evan422
OP
  

来学习学习
Evan422
OP
  


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

BlackBox32位
您需要登录后才可以回帖 登录 | 立即注册

返回顶部