Author Topic: [Tutorial] Calculating Checksums for Logical Volumes in Java  (Read 384 times)

0 Members and 1 Guest are viewing this topic.

Offline Psycho_Coder

  • Knight
  • **
  • Posts: 166
  • Cookies: 84
  • Programmer, Forensic Analyst
    • View Profile
    • Code Hackers Blog
[Tutorial] Calculating Checksums for Logical Volumes in Java
« on: January 30, 2016, 08:37:43 pm »
Hello EZ,

In this post I will discuss how to get checksums/hash of a logical volume in an OS. There are several tools available which provides similar features but most of them are coded in C++/C# (maybe I am not sure). Some flavors of Linux comes packed with CLI tools like dc3dd/dcfldd which provides on the fly hash generation. I wanted to achieve this volume hash generation using java and after googling quite a bit I didn't quite find any good tutorials or references but I found something interesting  here.

Thereafter I tried to access this drive as a file in Java and once I have a file object, its easier to get the checksum using java's builtin MessageDigest class. The only catch being that you have to execute the code using admin privileges. The code I will be sharing now has been tested on windows and the resulting hash obtained has been verified by calculating the hash for a particular drive using a hex editor (Winhex).

1. Task One: Get list of all Logical Drives

Code: (java) [Select]
File[] drives = File.listRoots(); //Returns the system drive letters.

If you're interested interested in knowing the type of drive then use the following snippet:

Code: (java) [Select]
FileSystemView fsv = FileSystemView.getFileSystemView();
        for (File f : drives){
            System.out.println(fsv.getSystemTypeDescription(f));           
        }

The above outputs something like the following where Local Disk means a logical volume (namely C:\, D:\ etc.) To get the display name for each drive use the function getSystemDisplayName from FileSystemView class.

Code: [Select]
Local Disk
Local Disk
Local Disk
CD Drive

1. Task Two: Understanding how to access the drives as file objects.

According to the link I gave earlier to get raw access to logical drives we should map the path as \\.\[Drive Letter]. Say if we have a function getVolumeHash(File file, String hashAlgo) where the first parameter is the complete path to the logical volume then we will pass the arguments as follows:

Code: (java) [Select]
File compLoc = new File("\\\\.\\" + drives[0].toString());
byte[] mdbytes = getVolumeHash(compLoc, "SHA1"); //Let's take SHA1 for now.

The above will return the digest of the volume as an array of bytes which we can convert to hex using the following method.

Code: (java) [Select]
/**
     * Converts array of bytes to hex string.
     *
     * @param bytes Byte Array to be converted to Hex String.
     * @return Returns the hex string for {@code bytes} array.
     */
    public static String byteArrayToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).
                    substring(1));
        }
        return sb.toString();
    }

So, Here the complete code (Only class):-

Code: (java) [Select]

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 *
 * @author Psycho_Coder
 */
public class DigestDiskVolume {

    /**
     * Converts array of bytes to hex string.
     *
     * @param bytes Byte Array to be converted to Hex String.
     * @return Returns the hex string for {@code bytes} array.
     */
    public static String byteArrayToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).
                    substring(1));
        }
        return sb.toString();
    }

    /**
     * Array of Logical Volumes
     *
     * @return returns File array of Logical Volumes
     */
    public static File[] getSystemLogicalVolumes() {
        return File.listRoots();
    }

    /**
     * <p>
     * Returns the hash of the file whose path and the type of Hashing Algo
     * scheme is passed as arguments to the method.</p>
     *
     * @param file Logical Volume which is to be hashed.
     * @param hashAlgo Hashing algorithm to be used.
     * @return returns Hex encoded hash of the file
     */
    public static String getVolumeHash(File file, String hashAlgo) {
        byte[] mdbytes = null;
        try (RandomAccessFile raf = new RandomAccessFile(file, "r");
                FileChannel fc = raf.getChannel();) {

            ByteBuffer buffer = ByteBuffer.allocate(4096);
            MessageDigest md = MessageDigest.getInstance(hashAlgo);

            while (fc.read(buffer) != -1) {
                buffer.flip();
                md.update(buffer);
                buffer.clear();
            }

            mdbytes = md.digest();
        } catch (NoSuchAlgorithmException | FileNotFoundException ex) {
            System.err.println(ex.getMessage());
        } catch (IOException ex) {
            System.err.println(ex.getMessage());
        }
        return byteArrayToHex(mdbytes);
    }
}


I believe the code performance can be increased if we use MappedByteBuffer. Well you can have a go. I hope you like this short tutorial and learned something new today. If you have some doubts please comment. I will try my best to answer your query.

Thanking you,
Sincerely,
Psycho_Coder.
"Don't do anything by half. If you love someone, love them with all your soul. When you hate someone, hate them until it hurts."--- Henry Rollins