Kỹ Thuật Phân Tích và Mô Phỏng Mã Hóa Yêu Cầu Trên Android

Lưu ý pháp lý

Bài viết dưới đây chỉ phục vụ mục đích nghiên cứu và chia sẻ kiến thức kỹ thuật. Người đọc không được sử dụng thông tin cho các hoạt động trái phép. Mọi hành vi gây thiệt hại đến quyền sở hữu trí tuệ của bên thứ ba đều do người thực hiện tự chịu trách nhiệm.

Mục tiêu phân tích

Trong quá trình tìm hiểu cơ chế bảo mật của ứng dụng di động, việc xác định cấu trúc tham số truyền tải là bước quan trọng. Bài hướng dẫn này sẽ chi tiết hóa quy trình từ thu thập dữ liệu mạng đến tái tạo lại thuật toán mã hóa đặc thù của ứng dụng cụ thể.

Tải xuống và chuẩn bị môi trường

  • Phiên bản target: v4.0.0
  • Xử lý file SO: Sau khi giải nén APK, cần xóa bỏ thư mục lib/arm64-v8a/libmsaoaidsec.so. File này thường chứa cơ chế phát hiện Frida, gây cản trở cho quá trình hook trên thiết bị thật hoặc máy ảo.

Khai thác lưu lượng mạng

Sử dụng công cụ proxy trung gian như Charles để bắt gói tin gửi đi từ ứng dụng. Trong các yêu cầu HTTP, tham số zqkd_param xuất hiện với độ dài bất thường, gợi ý rằng dữ liệu đã qua xử lý phức tạp.

Dựa vào kích thước chuỗi ký tự, giả thuyết đầu tiên là dữ liệu thô được mã hóa bằng thuật toán đối xứng (như AES) sau đó được chuyển đổi sang dạng Base64.

Chiến lược Hook động

Với Frida, mục tiêu là tìm ra điểm xử lý trước khi dữ liệu rời khỏi ứng dụng. Thông thường, các interceptor sẽ đóng vai trò trung gian chèn thêm tham số.

Truy vết hàm chặn (Interceptor)

Tập trung vào phương thức youthIntercept, nơi kiểm soát luồng dữ liệu ra ngoài. Biến kiểu Map trong hàm này chứa các cặp khóa-gia-trị dự kiến sẽ được mã hóa.

Nếu hook trực tiếp vào hàm getExtraParams ban đầu mà thấy giá trị hiển thị là Object, hãy ép kiểu về TreeMap để hiển thị chính xác nội dung.

Phân tích luồng điều khiển tại lớp nguồn:

// Mã nguồn gốc đã được làm rõ hơn
Map<String, String> thamSoThem = hamLayThamSoPhu(vanBanHienTai);
if (kiemTraNhanPhuongPhap("GET", tenPhuongThuc)) {
    // Xử lý sơ bộ tham số
    Map dataChuaKy = xuLyBangA(thamSoThem);
    
    // Bổ sung query param từ URL
    for (String key : urlNguon.queryParameterNames()) {
        dataChuaKy.put(key, urlNguon.queryParameter(key));
    }
    
    // Tạo chữ ký số từ map dữ liệu
    CapChuKy vanBanChinh = taoChuKy(dataChuaKy, vanBanHienTai);
    dataChuaKy.put(vanBanChinh.first, vanBanChinh.second);
    
    // Tiến hành mã hóa cuối cùng
    CapChuKy ketQuaMaHoa = maHoaDuLieu(dataChuaKy);
}

Qua phân tích, hai hàm trọng yếu là taoChuKy()maHoaDuLieu(). Việc thử nghiệm hook lần lượt cho thấy maHoaDuLieu() là bước quyết định kết quả cuối cùng.

Khám phá hàm mã hóa cốt lõi

Hàm maHoaDuLieu() có cấu trúc如下:

private final Pair<String, String> maHoaDuLieu(Map<String, String> nguonGoc) {
    // Chuyển đổi Map thành danh sách cặp
    DanhSachKetHop danhSachCap = new DanhSachKetHop();
    for (Map.Entry<String, String> cap : nguonGoc.entrySet()) {
        String giaTri = cap.getValue();
        if (!nguonCoThe(giaTri) && !giaTri.isEmpty()) {
            danhSachCap.add(new Pair(cap.getKey(), giaTri));
        }
    }
    // Gọi hàm tổng hợp và trả về
    return new Pair<>("zqkd_param", CongCuMang.xayDungThamSoTuYeuCau(danhSachCap));
}

Hàm gọi tiếp theo là CongCuMang.xayDungThamSoTuYeuCau(). Kết quả trả về tại đây khớp hoàn toàn với giá trị nằm trong gói tin bắt được.

Phân tích dòng lệnh mã hóa

Xét lại logic trong hàm xayDungThamSoTuYeuCau():

public static String xayDungThamSoTuYeuCau(List<Pair<String, String>> dsCap) {
    char kyTuNgauNhien = RandomMaVat.XuatKyTu();
    StringBuilder noiDung = new StringBuilder();
    if (dsCap != null && !dsCap.isEmpty()) {
        int tongSo = dsCap.size();
        for (int i = 0; i < tongSo; i++) {
            Pair<String, String> cp = dsCap.get(i);
            // Thêm dấu phân cách nếu không phải phần tử đầu
            if (i > 0) noiDung.append(XuDauPhanTac());
            noiDung.append(cp.first);
            noiDung.append(DauGiangGiaTri());
            noiDung.append(maHoaThuongThong(cp.second));
        }
    }
    // Quy trình bảo mật tổng hợp
    return RandomMaVat.LamXoahoi(
        StringUtils.khongRong(noiDung.toString()) 
        ? SecurityHelper.MaNguoiDungDES(
            ChuKyCongKhoa.layThongTin(MotChoApp.getContext(), kyTuNgauNhien), 
            noiDung.toString()
        ) : "", kyTuNgauNhien
    );
}

Luồng thực thi có thể đơn giản hóa thành 3 bước tuần tự:

  1. Tạo dữ liệu nền tảng: res1 = ChuKyCongKhoa.layThongTin(...)
  2. Mã hóa nội dung chính: res2 = SecurityHelper.MaNguoiDungDES(res1, noiDung)
  3. Thêm nhiễu ngẫu nhiên: ketQuaCuoi = RandomMaVat.LamXoahoi(res2, randomCode)

Tái hiện thuật toán DES

Sau khi tinh gọn, trọng tâm nằm ở phương thức MA_NGUOI_DUNG_DES. Đoạn mã này có thể tách ra chạy độc lập trong môi trường IDE chỉ với vài thay đổi nhỏ.

public static String MA_NGUOI_DUNG_DES(String chuoiKho, String duLieuToi) {
    try {
        byte[] tapByteKey = new byte[8];
        MessageDigest mdDao = MessageDigest.getInstance("MD5");
        mdDao.update(chuoiKho.getBytes());
        byte[] boNhoHash = mdDao.digest();
        
        // Chỉ lấy 8 byte đầu
        System.arraycopy(boNhoHash, 0, tapByteKey, 0, 8);
        
        String base64Tam = new String(Base64.encode(tapByteKey));
        
        // Khởi tạo vector khởi tạo IV
        IvParameterSpec ivParam = new IvParameterSpec(base64Tam.substring(0, 8).getBytes());
        
        // Khóa bí mật cho DES
        SecretKey secretKeyDia = SecretKeyFactory.getInstance("DES")
                .generateSecret(new DESKeySpec(chuoiKho.getBytes()));
        
        Cipher cipherDes = Cipher.getInstance("DES/CBC/PKCS5Padding");
        cipherDes.init(Cipher.ENCRYPT_MODE, secretKeyDia, ivParam);
        
        // Kết quả là IV (base64) + Dữ liệu đã mã hóa (base64)
        return base64Tam + new String(Base64.encode(cipherDes.doFinal(duLieuToi.getBytes())));
    } catch (Exception loi) {
        return null;
    }
}

Với biến đầu vào cố định cho phần seed (khoá ngẫu nhiên) và dữ liệu truyền vào, kết quả mã hóa hoàn toàn có thể tái tạo chính xác bằng Java hoặc Python để dùng trong các script tự động.

Thẻ: android-reverse-engineering frida des-encryption http-parsing mobile-security

Đăng vào ngày 2 tháng 6 lúc 20:54