Các kết quả khác nhau với thông báo của Java so với các tiện ích bên ngoài


194

Tôi đã viết một lớp Java đơn giản để tạo các giá trị băm của tệp Máy tính Windows. Tôi đang sử dụng Windows 7 Professional with SP1. Tôi đã thử Java 6.0.29Java 7.0.03. Ai đó có thể cho tôi biết lý do tại sao tôi nhận được các giá trị băm khác nhau từ Java so với (nhiều!) Các tiện ích và / hoặc trang web bên ngoài không? Mọi thứ bên ngoài khớp với nhau, chỉ có Java là trả về các kết quả khác nhau.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.CRC32;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Checksum 
{
    private static int size = 65536;
    private static File calc = new File("C:/Windows/system32/calc.exe");

    /*
        C:\Windows\System32\calc.exe (verified via several different utilities)
        ----------------------------
        CRC-32b = 8D8F5F8E
        MD5     = 60B7C0FEAD45F2066E5B805A91F4F0FC
        SHA-1   = 9018A7D6CDBE859A430E8794E73381F77C840BE0
        SHA-256 = 80C10EE5F21F92F89CBC293A59D2FD4C01C7958AACAD15642558DB700943FA22
        SHA-384 = 551186C804C17B4CCDA07FD5FE83A32B48B4D173DAC3262F16489029894FC008A501B50AB9B53158B429031B043043D2
        SHA-512 = 68B9F9C00FC64DF946684CE81A72A2624F0FC07E07C0C8B3DB2FAE8C9C0415BD1B4A03AD7FFA96985AF0CC5E0410F6C5E29A30200EFFF21AB4B01369A3C59B58


        Results from this class
        -----------------------
        CRC-32  = 967E5DDE
        MD5     = 10E4A1D2132CCB5C6759F038CDB6F3C9
        SHA-1   = 42D36EEB2140441B48287B7CD30B38105986D68F
        SHA-256 = C6A91CBA00BF87CDB064C49ADAAC82255CBEC6FDD48FD21F9B3B96ABF019916B    
    */    

    public static void main(String[] args)throws Exception {
        Map<String, String> hashes = getFileHash(calc);
        for (Map.Entry<String, String> entry : hashes.entrySet()) {
            System.out.println(String.format("%-7s = %s", entry.getKey(), entry.getValue()));
        }
    }

    private static Map<String, String> getFileHash(File file) throws NoSuchAlgorithmException, IOException {
        Map<String, String> results = new LinkedHashMap<String, String>();

        if (file != null && file.exists()) {
            CRC32 crc32 = new CRC32();
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
            MessageDigest sha256 = MessageDigest.getInstance("SHA-256");

            FileInputStream fis = new FileInputStream(file);
            byte data[] = new byte[size];
            int len = 0;
            while ((len = fis.read(data)) != -1) {
                crc32.update(data, 0, len);
                md5.update(data, 0, len);
                sha1.update(data, 0, len);
                sha256.update(data, 0, len);
            }
            fis.close();

            results.put("CRC-32", toHex(crc32.getValue()));
            results.put(md5.getAlgorithm(), toHex(md5.digest()));
            results.put(sha1.getAlgorithm(), toHex(sha1.digest()));
            results.put(sha256.getAlgorithm(), toHex(sha256.digest()));
        }
        return results;
    }

    private static String toHex(byte[] bytes) {
        String result = "";
        if (bytes != null) {
            StringBuilder sb = new StringBuilder(bytes.length * 2);
            for (byte element : bytes) {
                if ((element & 0xff) < 0x10) {
                    sb.append("0");
                }
                sb.append(Long.toString(element & 0xff, 16));
            }
            result = sb.toString().toUpperCase();
        }
        return result;
    }

    private static String toHex(long value) {
        return Long.toHexString(value).toUpperCase();
    }

}

Tôi đoán toHex của bạn là sai. Nếu bạn làm int newElement = ((int) element) & 0xffvà sử dụng điều đó thay vào đó sẽ giải quyết vấn đề của bạn?
zapl

64
Song song với việc tính tổng kiểm tra, sao chép tệp vào một số tệp tạm thời để bạn có thể so sánh những gì Java nhận được với những gì bạn nhận được khi bạn sử dụng các công cụ khác. Windows có thể kỳ lạ như thế ... Tôi chưa bao giờ thấy Java mắc lỗi khi tính băm ...
Pawel Veselov

3
Tất cả các lập trình viên nên lập trình như thế này! Mã rất sạch sẽ và gọn gàng.
Martijn Courteaux

2
@ user567496: đối với giá trị mà mã của bạn cung cấp cho băm SHA-1 chính xác so với triển khai Java SHA-1 khác và so với dòng lệnh sha1sum sử dụng ... (đã thử nghiệm với các tệp trên Linux, không phải với calc.exe)
TacticalCoder

1
@Fido: trong trường hợp này không phải là vấn đề về bộ ký tự vì OP đang đọc byte thô: anh ấy không giải mã được các ký tự.
chiến thuật

Câu trả lời:


239

Hiểu rồi. Hệ thống tệp Windows đang hoạt động khác nhau tùy thuộc vào kiến ​​trúc của quy trình của bạn. Bài viết này giải thích tất cả - cụ thể:

Nhưng những gì về các ứng dụng 32 bit có đường dẫn hệ thống được mã hóa cứng và đang chạy trong Windows 64 bit thì sao? Làm thế nào họ có thể tìm thấy thư mục SysWOW64 mới mà không thay đổi mã chương trình, bạn có thể nghĩ. Câu trả lời là trình giả lập chuyển hướng các cuộc gọi đến thư mục System32 đến thư mục SysWOW64 một cách trong suốt nên ngay cả khi thư mục được mã hóa cứng vào thư mục System32 (như C: \ Windows \ System32), trình giả lập sẽ đảm bảo rằng thư mục SysWOW64 được sử dụng thay thế . Vì vậy, cùng một mã nguồn, sử dụng thư mục System32, có thể được biên dịch thành cả mã chương trình 32 bit và 64 bit mà không có bất kỳ thay đổi nào.

Hãy thử sao chép calc.exesang một nơi khác ... sau đó chạy lại các công cụ tương tự. Bạn sẽ nhận được kết quả tương tự như Java. Một cái gì đó về hệ thống tệp Windows đang cung cấp dữ liệu khác nhau cho các công cụ so với việc cung cấp cho Java ... Tôi chắc chắn rằng đó là một cái gì đó để làm với nó trong thư mục Windows, và do đó có thể xử lý "khác nhau".

Hơn nữa, tôi đã sao chép nó trong C # ... và phát hiện ra rằng nó phụ thuộc vào kiến trúc của quy trình bạn đang chạy . Vì vậy, đây là một chương trình mẫu:

using System;
using System.IO;
using System.Security.Cryptography;

class Test
{
    static void Main()
    {
        using (var md5 = MD5.Create())
        {
            string path = "c:/Windows/System32/Calc.exe";
            var bytes = md5.ComputeHash(File.ReadAllBytes(path));
            Console.WriteLine(BitConverter.ToString(bytes));
        }
    }
}

Và đây là phiên giao diện điều khiển (trừ cuộc trò chuyện từ trình biên dịch):

c:\users\jon\Test>csc /platform:x86 Test.cs    

c:\users\jon\Test>test
60-B7-C0-FE-AD-45-F2-06-6E-5B-80-5A-91-F4-F0-FC

c:\users\jon\Test>csc /platform:x64 Test.cs

c:\users\jon\Test>test
10-E4-A1-D2-13-2C-CB-5C-67-59-F0-38-CD-B6-F3-C9

64
Có hai phiên bản calc.exe: 64 bit trong C:\Windows\system32` and 32bit in C: \ Windows \ SysWOW64`. Để tương thích trong quy trình 32 bit C:\Windows\system32` is mapped to C: \ Windows \ SysWOW64`. Các quy trình 64 bit sẽ khởi chạy calc 64 bit, 32 bit xử lý calc 32 bit. Không ngạc nhiên khi tổng kiểm tra của họ là khác nhau. Nếu bạn giữ tệp mở và tìm với handles.exehoặc Process Explorer, bạn sẽ thấy đường dẫn khác.
Richard

25
@Jon Cái gì đó được gọi là Bộ chuyển hướng hệ thống tệp.
David Heffernan

9
@DavidHeffernan Ý kiến ​​khác nhau, có lẽ cùng với định nghĩa 'khả thi'. Tất cả ảo hóa này không vi phạm nguyên tắc ít bất ngờ nhất và thêm chi phí (phân bổ và thời gian chạy). Các hệ điều hành khác quản lý để cung cấp cả hỗ trợ 32 trên 64 tốt hơn và ảo hóa ứng dụng tốt hơn với ít tóm tắt / trừu tượng hơn (thử chạy các chương trình thu gom rác trên Wow64 hoặc thử so sánh các khoản tiền md5 như OP và một vài trường hợp thích hợp khác).
sehe

5
Đôi khi tôi tự hỏi nếu mọi người đánh giá cao bạn bởi vì bạn là người đa nghi, không chỉ vì câu trả lời. Tôi không nói câu trả lời là không tốt hay bất cứ điều gì, nhưng 145 câu trả lời khi câu trả lời là "Một cái gì đó đang xảy ra trong cửa sổ" (công bằng mà bạn cung cấp một liên kết, nhưng vẫn) có vẻ như mọi người đang xem xét nhiều hơn chỉ là câu trả lời của bạn khi họ upvote. Tôi không ghét bạn, nhưng điều này chỉ có nghĩa là sẽ mất một lúc trước khi tôi bắt kịp bạn: P
Jason Ridge

5
Blog là cách tôi tìm thấy nó. Tôi đã hy vọng vào một số phép thuật Jon Skeet nhưng tôi cảm thấy như "Này, tôi có thể đã làm điều đó". Có lẽ không nhanh như vậy nhưng có bạn đi. Ok có lẽ tôi không thể có, nhưng vẫn còn. Đối với nắp, có rất ít sự an ủi trong đó bởi vì điều đó chỉ có nghĩa là bất kỳ ngày nào bạn sẽ đạt được nó, và do đó tôi không bao giờ có thể bắt kịp bạn. Ôi chà ...
Jason Ridge
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.