Attribute VB_Name = "modPEDifferCLI"
Option Explicit

'Command Line PE Differ
'David's Malware Analysis Toolkit
'
'Usage from VB6 IDE immediate window or external automation:
'  Call ComparePEFiles("file1.exe", "file2.exe")
'  Call ComparePEFiles("file1.exe", "file2.exe", "report.txt")
'  Call BatchCompareFolder("C:\samples\", "*.exe")

Public Sub ComparePEFiles(file1 As String, file2 As String, Optional reportFile As String = "")
    On Error GoTo hell
    
    Dim pe1 As New CPEEditor
    Dim pe2 As New CPEEditor
    Dim differ As New CPEDiffer
    
    'Load files
    Debug.Print "Loading: " & file1
    If Not pe1.LoadFile(file1) Then
        Debug.Print "ERROR: Failed to load file1 - " & pe1.errMessage
        Exit Sub
    End If
    
    Debug.Print "Loading: " & file2
    If Not pe2.LoadFile(file2) Then
        Debug.Print "ERROR: Failed to load file2 - " & pe2.errMessage
        Exit Sub
    End If
    
    'Compare
    Debug.Print "Comparing files..."
    If Not differ.Compare(pe1, pe2) Then
        Debug.Print "ERROR: Comparison failed"
        Exit Sub
    End If
    
    'Generate report
    Dim report As String
    report = differ.GetTextReport
    
    'Output
    If Len(reportFile) > 0 Then
        'Save to file
        Dim ff As Integer
        ff = FreeFile
        Open reportFile For Output As #ff
        Print #ff, report
        Close #ff
        Debug.Print "Report saved to: " & reportFile
    Else
        'Print to debug window
        Debug.Print report
    End If
    
    'Quick summary
    If differ.HasChanges Then
        Debug.Print vbCrLf & "SUMMARY: Found " & differ.DiffCount & " difference(s)"
        PrintCategorySummary differ
    Else
        Debug.Print vbCrLf & "SUMMARY: Files are identical"
    End If
    
    Exit Sub
hell:
    Debug.Print "ERROR: " & Err.Description
End Sub

Public Sub BatchCompareFolder(folderPath As String, Optional pattern As String = "*.exe")
    'Compare all files matching pattern against first file as baseline
    On Error GoTo hell
    
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    If Not fso.FolderExists(folderPath) Then
        Debug.Print "ERROR: Folder not found: " & folderPath
        Exit Sub
    End If
    
    'Get all matching files
    Dim files As Collection
    Set files = GetMatchingFiles(folderPath, pattern)
    
    If files.count < 2 Then
        Debug.Print "ERROR: Need at least 2 files to compare"
        Exit Sub
    End If
    
    Debug.Print "Found " & files.count & " files to compare"
    Debug.Print String(80, "=")
    
    'Use first file as baseline
    Dim baseline As String
    baseline = files(1)
    Debug.Print "Baseline: " & fso.GetFileName(baseline)
    Debug.Print String(80, "=")
    Debug.Print ""
    
    'Compare all others against baseline
    Dim i As Long
    For i = 2 To files.count
        CompareFilesQuick baseline, files(i)
        Debug.Print ""
    Next
    
    Exit Sub
hell:
    Debug.Print "ERROR: " & Err.Description
End Sub

Public Sub CompareFilesQuick(file1 As String, file2 As String)
    'Quick comparison with summary only (no detailed report)
    On Error Resume Next
    
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    Debug.Print "Comparing: " & fso.GetFileName(file2)
    
    Dim pe1 As New CPEEditor
    Dim pe2 As New CPEEditor
    Dim differ As New CPEDiffer
    
    If Not pe1.LoadFile(file1) Then
        Debug.Print "  ERROR: Failed to load baseline"
        Exit Sub
    End If
    
    If Not pe2.LoadFile(file2) Then
        Debug.Print "  ERROR: Failed to load target"
        Exit Sub
    End If
    
    If Not differ.Compare(pe1, pe2) Then
        Debug.Print "  ERROR: Comparison failed"
        Exit Sub
    End If
    
    If differ.HasChanges Then
        Debug.Print "  Differences: " & differ.DiffCount
        
        'Category breakdown
        Debug.Print "    File Info: " & differ.GetDifferencesByCategory(dcFileInfo).count
        Debug.Print "    Optional Header: " & differ.GetDifferencesByCategory(dcOptionalHeader).count
        Debug.Print "    Sections: " & differ.GetDifferencesByCategory(dcSections).count
        Debug.Print "    Imports: " & differ.GetDifferencesByCategory(dcImports).count
        Debug.Print "    Exports: " & differ.GetDifferencesByCategory(dcExports).count
        Debug.Print "    Resources: " & differ.GetDifferencesByCategory(dcResources).count
        Debug.Print "    Relocations: " & differ.GetDifferencesByCategory(dcRelocations).count
        Debug.Print "    Debug Info: " & differ.GetDifferencesByCategory(dcDebugInfo).count
    Else
        Debug.Print "  IDENTICAL"
    End If
End Sub

Public Sub CompareWithImphashFilter(folder1 As String, folder2 As String)
    'Compare two folders of PE files, but only do detailed comparison
    'when imphash differs (optimization for large batches)
    On Error GoTo hell
    
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    If Not fso.FolderExists(folder1) Or Not fso.FolderExists(folder2) Then
        Debug.Print "ERROR: One or both folders not found"
        Exit Sub
    End If
    
    Dim files1 As Collection, files2 As Collection
    Set files1 = GetMatchingFiles(folder1, "*.exe")
    Set files2 = GetMatchingFiles(folder2, "*.exe")
    
    Debug.Print "Comparing " & files1.count & " files from folder1 vs " & files2.count & " from folder2"
    Debug.Print String(80, "=")
    
    'Build imphash index for folder2
    Dim imphashIndex As Object
    Set imphashIndex = CreateObject("Scripting.Dictionary")
    
    Dim pe As New CPEEditor
    Dim i As Long, fname As String
    
    Debug.Print "Building imphash index..."
    For i = 1 To files2.count
        If pe.LoadFile(files2(i)) Then
            fname = fso.GetFileName(files2(i))
            imphashIndex(pe.impHash) = files2(i)
        End If
    Next
    
    Debug.Print "Comparing files..."
    Debug.Print ""
    
    'Compare each file in folder1
    For i = 1 To files1.count
        If pe.LoadFile(files1(i)) Then
            fname = fso.GetFileName(files1(i))
            Dim hash1 As String
            hash1 = pe.impHash
            
            If imphashIndex.Exists(hash1) Then
                Debug.Print fname & " - Imphash match found"
                Debug.Print "  Match: " & fso.GetFileName(imphashIndex(hash1))
                
                'Do detailed comparison
                Dim pe2 As New CPEEditor
                If pe2.LoadFile(imphashIndex(hash1)) Then
                    Dim differ As New CPEDiffer
                    If differ.Compare(pe, pe2) Then
                        If differ.HasChanges Then
                            Debug.Print "  Detailed differences: " & differ.DiffCount
                        Else
                            Debug.Print "  Files are identical!"
                        End If
                    End If
                End If
            Else
                Debug.Print fname & " - No imphash match (unique imports)"
            End If
        End If
    Next
    
    Exit Sub
hell:
    Debug.Print "ERROR: " & Err.Description
End Sub

Public Sub ExportDifferencesToCSV(file1 As String, file2 As String, csvFile As String)
    'Export differences to CSV for spreadsheet analysis
    On Error GoTo hell
    
    Dim pe1 As New CPEEditor
    Dim pe2 As New CPEEditor
    Dim differ As New CPEDiffer
    
    pe1.LoadFile file1
    pe2.LoadFile file2
    
    If Not differ.Compare(pe1, pe2) Then
        Debug.Print "ERROR: Comparison failed"
        Exit Sub
    End If
    
    'Open CSV file
    Dim ff As Integer
    ff = FreeFile
    Open csvFile For Output As #ff
    
    'Header
    Print #ff, "Category,Field,Original Value,New Value"
    
    'Write differences
    Dim diffs As Collection
    Set diffs = differ.GetDifferences
    
    Dim i As Long
    Dim d As CDiffEntry
    For i = 1 To diffs.count
        Set d = diffs(i)
        Print #ff, GetCategoryName(d.category) & "," & _
                   CSVEscape(d.fieldName) & "," & _
                   CSVEscape(d.value1) & "," & _
                   CSVEscape(d.value2)
    Next
    
    Close #ff
    Debug.Print "CSV exported to: " & csvFile
    Debug.Print "Total rows: " & diffs.count
    
    Exit Sub
hell:
    Debug.Print "ERROR: " & Err.Description
End Sub

'============================================================================
' HELPER FUNCTIONS
'============================================================================

Private Function GetMatchingFiles(folderPath As String, pattern As String) As Collection
    Dim fso As Object, folder As Object, file As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set folder = fso.GetFolder(folderPath)
    
    Dim result As New Collection
    
    For Each file In folder.files
        If LCase(file.Name) Like LCase(pattern) Then
            result.Add file.path
        End If
    Next
    
    Set GetMatchingFiles = result
End Function

Private Sub PrintCategorySummary(differ As CPEDiffer)
    Debug.Print ""
    Debug.Print "By Category:"
    
    Dim cat As Long
    For cat = 0 To 7
        Dim count As Long
        count = differ.GetDifferencesByCategory(cat).count
        If count > 0 Then
            Debug.Print "  " & PadRight(GetCategoryName(cat), 20) & ": " & count
        End If
    Next
End Sub

Private Function GetCategoryName(cat As Long) As String
    Select Case cat
        Case 0: GetCategoryName = "File Info"
        Case 1: GetCategoryName = "Optional Header"
        Case 2: GetCategoryName = "Sections"
        Case 3: GetCategoryName = "Imports"
        Case 4: GetCategoryName = "Exports"
        Case 5: GetCategoryName = "Resources"
        Case 6: GetCategoryName = "Relocations"
        Case 7: GetCategoryName = "Debug Info"
        Case 8: GetCategoryName = "Rich Header"
        Case Else: GetCategoryName = "Unknown"
    End Select
End Function

Private Function PadRight(s As String, length As Long) As String
    If Len(s) >= length Then
        PadRight = s
    Else
        PadRight = s & Space(length - Len(s))
    End If
End Function

Private Function CSVEscape(s As String) As String
    'Escape CSV special characters
    If InStr(s, ",") > 0 Or InStr(s, """") > 0 Or InStr(s, vbCrLf) > 0 Then
        CSVEscape = """" & Replace(s, """", """""") & """"
    Else
        CSVEscape = s
    End If
End Function

'============================================================================
' IMMEDIATE WINDOW QUICK COMMANDS
'============================================================================

'Quick diff two files:
'  Call ComparePEFiles("file1.exe", "file2.exe")

'Save report to file:
'  Call ComparePEFiles("file1.exe", "file2.exe", "report.txt")

'Compare all EXEs in folder:
'  Call BatchCompareFolder("C:\samples\", "*.exe")

'Export to CSV:
'  Call ExportDifferencesToCSV("file1.exe", "file2.exe", "diff.csv")

'Compare with imphash optimization:
'  Call CompareWithImphashFilter("C:\samples\set1\", "C:\samples\set2\")
