rbindiff 2025.6.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +128 -0
- data/bin/rbindiff +5 -0
- data/lib/rbindiff.rb +149 -0
- metadata +75 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5478fd0461693bf21baa0ba67a10315193e2793de656674c23ecc9af0636a438
|
4
|
+
data.tar.gz: 55a25484dc6e29a307365c0e93b210ae78ee0554ee040e1a5a2284c85303bba1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: aea6f0cca7edfc1ac9066d87c6607f706c821a19324ab88daec3bebbb987382defe7f70ef1f833783014eaa891805bbcf2af65b10de9db74d920fccc600ba944
|
7
|
+
data.tar.gz: f19a8213d5adf16be22672bf7eed12e4cfeee6114106aac86049fee534b3c8fe3dc4773a9b99dc60a9bb937ae174de9584c2c21e7caf78ad5c01a771a8cc92ce
|
data/README.md
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# ð Welcome to **rbindiff** â A Binary File Comparison Tool! ð
|
2
|
+
|
3
|
+
You're about to build a powerful and useful gem called `rbindiff` that compares binary files byte by byte, highlights differences, and calculates similarity ratios. This README will guide you through the project structure, how it works, and how others can install and use it.
|
4
|
+
|
5
|
+
---
|
6
|
+
|
7
|
+
## ð What is rbindiff?
|
8
|
+
|
9
|
+
**rbindiff** stands for "Ruby Binary Diff". It's a command-line tool written in Ruby that:
|
10
|
+
|
11
|
+
- Compares two binary files byte-by-byte.
|
12
|
+
- Outputs a human-readable summary of:
|
13
|
+
- Ranges where the files are **identical**
|
14
|
+
- Ranges where they **differ**, with short hex representations
|
15
|
+
- Extra content at the end if one file is longer than the other
|
16
|
+
- Calculates **similarity percentage** based on matching bytes.
|
17
|
+
|
18
|
+
---
|
19
|
+
|
20
|
+
## ð§ Installation
|
21
|
+
|
22
|
+
```bash
|
23
|
+
gem install rbindiff
|
24
|
+
```
|
25
|
+
|
26
|
+
Once installed, you'll have access to the `rbindiff` executable in your terminal.
|
27
|
+
|
28
|
+
---
|
29
|
+
|
30
|
+
## ðĶ Usage
|
31
|
+
|
32
|
+
```bash
|
33
|
+
rbindiff <file1> <file2>
|
34
|
+
```
|
35
|
+
|
36
|
+
### Example:
|
37
|
+
|
38
|
+
```bash
|
39
|
+
rbindiff image1.png image2.png
|
40
|
+
```
|
41
|
+
|
42
|
+
---
|
43
|
+
|
44
|
+
## âĻ Sample Output
|
45
|
+
|
46
|
+
```
|
47
|
+
Comparing: image1.png vs image2.png
|
48
|
+
|
49
|
+
[Identical] Bytes 0x0000 to 0x00FF (256 bytes)
|
50
|
+
[Different] Bytes 0x0100 to 0x010F (16 bytes)
|
51
|
+
File1: 48 65 6C 6C 6F 20 77 6F ... 6E 67 20 77 6F 72 6C 64
|
52
|
+
File2: 48 65 6C 6C 6F 20 77 6F ... 6E 67 20 57 4F 52 4C 44
|
53
|
+
|
54
|
+
[Identical] Bytes 0x0110 to 0x01FF (240 bytes)
|
55
|
+
[Extra] File2 has 32 extra bytes from 0x0200 to 0x021F
|
56
|
+
|
57
|
+
Similarity: 92.5% (same bytes) / 89.1% (relative to total lengths)
|
58
|
+
```
|
59
|
+
|
60
|
+
---
|
61
|
+
|
62
|
+
## ð ïļ Features
|
63
|
+
|
64
|
+
â
Byte-level comparison
|
65
|
+
â
Human-readable output
|
66
|
+
â
Hex dump of differing regions (with ellipsis)
|
67
|
+
â
Similarity percentages
|
68
|
+
â
Supports files of different lengths
|
69
|
+
â
Terminal-safe formatting
|
70
|
+
|
71
|
+
---
|
72
|
+
|
73
|
+
## ð Project Structure
|
74
|
+
|
75
|
+
```
|
76
|
+
rbindiff/
|
77
|
+
âââ bin/
|
78
|
+
â âââ rbindiff # Executable script
|
79
|
+
âââ lib/
|
80
|
+
â âââ rbindiff.rb # Main logic
|
81
|
+
âââ rbindiff.gemspec
|
82
|
+
âââ Gemfile
|
83
|
+
âââ README.md # You're reading this!
|
84
|
+
âââ .gitignore
|
85
|
+
```
|
86
|
+
|
87
|
+
---
|
88
|
+
|
89
|
+
## ð How It Works
|
90
|
+
|
91
|
+
The core algorithm works like this:
|
92
|
+
|
93
|
+
1. Open both files as binary streams.
|
94
|
+
2. Read them into byte arrays.
|
95
|
+
3. Compare byte-by-byte up to the minimum length.
|
96
|
+
4. Track ranges of identical or differing bytes.
|
97
|
+
5. If one file is longer, record the extra part.
|
98
|
+
6. Print the result with range info, diffs, and similarity ratio.
|
99
|
+
|
100
|
+
---
|
101
|
+
|
102
|
+
## ð§Ū Similarity Calculation
|
103
|
+
|
104
|
+
There are two values:
|
105
|
+
|
106
|
+
- `% Matched`: `(Same bytes) / min(file1.size, file2.size)`
|
107
|
+
- `% Relative`: `(Same bytes) / max(file1.size, file2.size)`
|
108
|
+
|
109
|
+
This gives an idea of how similar the common parts are, and how much of the larger file matches.
|
110
|
+
|
111
|
+
---
|
112
|
+
|
113
|
+
## ðĻ Color & Formatting (Optional)
|
114
|
+
|
115
|
+
If you want to enhance user experience later:
|
116
|
+
|
117
|
+
- Use ANSI escape codes to color-code differences
|
118
|
+
- Add progress bar or spinner for large files
|
119
|
+
|
120
|
+
---
|
121
|
+
|
122
|
+
## ð You've Got This!
|
123
|
+
|
124
|
+
Building tools like `rbindiff` helps developers debug binaries, reverse-engineer files, and understand low-level data formats. Your work could help many people â including future YOU.
|
125
|
+
|
126
|
+
Keep going! You're doing great ðŠ
|
127
|
+
|
128
|
+
Let me know when you're ready to write the code â I'd love to help you implement each part step by step!
|
data/bin/rbindiff
ADDED
data/lib/rbindiff.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
# lib/rbindiff.rb
|
2
|
+
|
3
|
+
module RBinDiff
|
4
|
+
def self.run(args)
|
5
|
+
if args.size != 2
|
6
|
+
puts "Usage: rbindiff <file1> <file2>"
|
7
|
+
exit 1
|
8
|
+
end
|
9
|
+
|
10
|
+
file1, file2 = args
|
11
|
+
|
12
|
+
unless File.exist?(file1) && File.readable?(file1)
|
13
|
+
puts "Error: Cannot read file1 '#{file1}'"
|
14
|
+
exit 1
|
15
|
+
end
|
16
|
+
|
17
|
+
unless File.exist?(file2) && File.readable?(file2)
|
18
|
+
puts "Error: Cannot read file2 '#{file2}'"
|
19
|
+
exit 1
|
20
|
+
end
|
21
|
+
|
22
|
+
content1 = File.binread(file1)
|
23
|
+
content2 = File.binread(file2)
|
24
|
+
|
25
|
+
compare_and_report(content1, content2)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.compare_and_report(content1, content2)
|
29
|
+
offset = 0
|
30
|
+
results = []
|
31
|
+
max_length = [content1.bytesize, content2.bytesize].max
|
32
|
+
chunk_size = 1024 * 1024 # æŊ1MBæīæ°äļæŽĄčŋåšĶ
|
33
|
+
|
34
|
+
print "Progress: [............] 0%\r"
|
35
|
+
|
36
|
+
while offset < max_length
|
37
|
+
b1 = offset < content1.bytesize ? content1.getbyte(offset) : nil
|
38
|
+
b2 = offset < content2.bytesize ? content2.getbyte(offset) : nil
|
39
|
+
same = b1 == b2
|
40
|
+
|
41
|
+
range_start = offset
|
42
|
+
|
43
|
+
if same
|
44
|
+
while offset < max_length
|
45
|
+
b1 = offset < content1.bytesize ? content1.getbyte(offset) : nil
|
46
|
+
b2 = offset < content2.bytesize ? content2.getbyte(offset) : nil
|
47
|
+
break unless b1 == b2
|
48
|
+
offset += 1
|
49
|
+
end
|
50
|
+
else
|
51
|
+
while offset < max_length
|
52
|
+
b1 = offset < content1.bytesize ? content1.getbyte(offset) : nil
|
53
|
+
b2 = offset < content2.bytesize ? content2.getbyte(offset) : nil
|
54
|
+
break if b1 == b2
|
55
|
+
offset += 1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
results << {
|
60
|
+
same: same,
|
61
|
+
start: range_start,
|
62
|
+
finish: offset - 1
|
63
|
+
}
|
64
|
+
|
65
|
+
# æīæ°čŋåšĶæĄ
|
66
|
+
if offset % chunk_size == 0 || offset == max_length
|
67
|
+
progress = offset.to_f / max_length * 100
|
68
|
+
filled = ('=' * (progress / 5)).ljust(20)
|
69
|
+
print "Progress: [#{filled}] %.1f%%\r" % progress
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
print "Progress: [====================] 100.0%\n"
|
74
|
+
|
75
|
+
# åĪįéŋåšĶäļäļčīįæ
åĩ
|
76
|
+
if content1.bytesize != content2.bytesize
|
77
|
+
shorter = [content1.bytesize, content2.bytesize].min
|
78
|
+
longer = [content1.bytesize, content2.bytesize].max
|
79
|
+
file_label = content1.bytesize > content2.bytesize ? "File1" : "File2"
|
80
|
+
results << { same: nil, start: shorter, finish: longer - 1, file: file_label }
|
81
|
+
end
|
82
|
+
|
83
|
+
print_results(results, content1, content2)
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.print_results(results, content1, content2)
|
87
|
+
total_same_bytes = 0
|
88
|
+
total_diff_bytes = 0
|
89
|
+
|
90
|
+
results.each do |result|
|
91
|
+
if result[:same] == true
|
92
|
+
total_same_bytes += result[:finish] - result[:start] + 1
|
93
|
+
elsif result[:same] == false
|
94
|
+
total_diff_bytes += result[:finish] - result[:start] + 1
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
len1 = content1.bytesize
|
99
|
+
len2 = content2.bytesize
|
100
|
+
min_len = [len1, len2].min
|
101
|
+
max_len = [len1, len2].max
|
102
|
+
|
103
|
+
percent_matched = (total_same_bytes.to_f / min_len) * 100
|
104
|
+
percent_relative = (total_same_bytes.to_f / max_len) * 100
|
105
|
+
|
106
|
+
puts "Comparing files of size #{len1} and #{len2} bytes:"
|
107
|
+
puts
|
108
|
+
|
109
|
+
results.each do |result|
|
110
|
+
start = format("0x%04X", result[:start])
|
111
|
+
finish = format("0x%04X", result[:finish])
|
112
|
+
|
113
|
+
if result[:same] == true
|
114
|
+
puts "[Identical] Bytes #{start} to #{finish} (#{result[:finish] - result[:start] + 1} bytes)"
|
115
|
+
elsif result[:same] == false
|
116
|
+
puts "[Different] Bytes #{start} to #{finish} (#{result[:finish] - result[:start] + 1} bytes)"
|
117
|
+
|
118
|
+
hex1 = content1.byteslice(result[:start], result[:finish] - result[:start] + 1).bytes.map { |b| format("%02X", b) }
|
119
|
+
hex2 = content2.byteslice(result[:start], result[:finish] - result[:start] + 1).bytes.map { |b| format("%02X", b) }
|
120
|
+
|
121
|
+
display_hex(hex1, "File1")
|
122
|
+
display_hex(hex2, "File2")
|
123
|
+
else
|
124
|
+
file_label = result[:file]
|
125
|
+
puts "[Extra] #{file_label} has #{result[:finish] - result[:start] + 1} extra bytes from #{start} to #{finish}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
puts
|
130
|
+
puts "Different bytes: #{total_diff_bytes} byte(s)"
|
131
|
+
puts "Similarity: %.5f%% (matched / min length) / %.5f%% (matched / max length)" % [
|
132
|
+
percent_matched,
|
133
|
+
percent_relative
|
134
|
+
]
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.display_hex(bytes_array, label)
|
138
|
+
return if bytes_array.empty?
|
139
|
+
|
140
|
+
if bytes_array.size <= 16
|
141
|
+
line = bytes_array.join(" ")
|
142
|
+
puts "#{label}: #{line}"
|
143
|
+
else
|
144
|
+
head = bytes_array[0..7].join(" ")
|
145
|
+
tail = bytes_array[-8..-1].join(" ")
|
146
|
+
puts "#{label}: #{head} ... #{tail}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rbindiff
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2025.6.21
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hxcan Cai
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-06-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '13.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '13.0'
|
41
|
+
description: Compare two binary files byte-by-byte and show differences in hex.
|
42
|
+
email:
|
43
|
+
- caihuosheng@email.com
|
44
|
+
executables:
|
45
|
+
- rbindiff
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- README.md
|
50
|
+
- bin/rbindiff
|
51
|
+
- lib/rbindiff.rb
|
52
|
+
homepage: https://github.com/hxcan/rbindiff
|
53
|
+
licenses:
|
54
|
+
- MIT
|
55
|
+
metadata: {}
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubygems_version: 3.5.22
|
72
|
+
signing_key:
|
73
|
+
specification_version: 4
|
74
|
+
summary: A binary file comparison tool
|
75
|
+
test_files: []
|