I am a big fan of plain text. It is easy to view and easy to edit. Unfortunately, it is sometimes necessary to work with binary files and data.
Ruby’s inspect method does a decent job of showing the contents of a binary string, but sometimes I need something a little more powerful.
Back in the day I would use a hex editor to open binary files and decipher their contents. I don’t have a need for a hex editor anymore, but I would like to occasionally view binary data in the same format.
After some intense Googling and Ruby doc reading, I came up with a few methods to convert a binary string to hex, and convert a string of hex back to the original binary.
Bin to Hex
To convert a string to it hex representation, first take each byte, convert it to hex, then join all of the hex digits back together.
def bin_to_hex(s)
s.each_byte.map { |b| b.to_s(16) }.join
end
If you like spaces between the hex digits, change join to join(‘ ‘)
Hex to Bin
Converting the string of hex digits back to binary is just as easy. Take the hex digits two at a time (since each byte can range from 00 to FF), convert the digits to a character, and join them back together.
def hex_to_bin(s)
s.scan(/../).map { |x| x.hex.chr }.join
end
If you find yourself using these frequently in a project, you could add the methods to the String class.
TIMTOWTDI
Of course, there is more than one way to do this. Ruby also provides the handy pack and unpack methods for Arrays and Strings respectively. These are a little more cryptic since you need to know the meaning of the format string to understand what’s going on.
def bin_to_hex(s)
s.unpack('H*').first
end
def hex_to_bin(s)
s.scan(/../).map { |x| x.hex }.pack('c*')
end
Check the Ruby documentation for Array and String for a complete explanation of pack and unpack.
Examples
Here’s the output of a quick IRB session to demonstrate how this works.
irb(main):001:0> s = "Hello, World!"
=> "Hello, World!"
irb(main):002:0> s = s.each_byte.map { |b| b.to\_s(16) }.join
=> "48656c6c6f2c20576f726c6421"
irb(main):003:0> s = s.scan(/../).map { |x| x.hex.chr }.join
=> "Hello, World!"
These methods are no replacement for a hex editor, but if you need to check an encryption key or some other short string of binary, they can be just the thing.
This doesn’t work in all cases because the conversion from hex to bytes assumes 2 hex characters are produced for each byte, and that is not always the case, at least in Ruby 1.9.3. A byte value of 4 produces a one-character hex string, “4”, instead of “04” as the hex_to_bin expects. Additional logic is needed in bin_to_hex to prepend a zero in cases where the to_s(16) call results in only one character.
I’ve been bitten by using this binary to hex conversion several times now, as I forget that to_s(16) only produces the number of hexidecimal digits required for the conversion.
“x00x03”.each_byte.map { |b| b.to_s(16) }.join
=> “03”
That’s not quite what you’d expect. You have to use rjust (or something else) to obtain the desired result:
“x00x03”.each_byte.map { |b| b.to_s(16).rjust(2, ‘0’) }.join
=> “0003”
Thanks for your post!
Unfortunately I’m getting this error:
breaking_manytimepad.rb:2: syntax error, unexpected $undefined
s.each_byte.map { |b| b.to_s(16) }.join
I’ using ruby 2.1.0
06:25:30 x42@braegel2:~/cryptography1.git/week1 # ruby -v
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-linux
The “TIMTOWTDI” solutions worked for me 🙂 thanks!
i had to change bin_to_hex into this because the original version was loosing leading zeroes when converting binary to hex.
this for example:
y=10
print y.to_s(16)
will result in the letter a instead of “0a”.
the “%02x” string formatting can take care of this:
y=10
print “%02x” % y
will result in the expected “0a”.
here is the corrected bin_to_hex:
def bin_to_hex(s)
s.each_byte.map { |b| “%02x” % b.to_i }.join
end
it’s handy to have such routines when debugging crypto keys…..just as you mentioned.
Hi Bernd,
Just to clarify, that line of code in the bin_to_hex method has a typo:
s.each_byte.map { |b| b.to_s(16) }.join
The method is
to_s
There’s a cheeky and unwanted backslash that’s crept in.
You can also define hex2bin(s) as follows:
def hex2bin(s)
[self].pack('H*')
end
TMTOWTDI 🙂
Correction:
def hex2bin(s)
[s].pack('H*')
end
Previous example was part of a String class 🙂
Apropos of this subject, I’ve gone further and produced a module that can produce nicely outputted format for logging, debugging, etc., as in:
# Sample Output:
# require 'trick_bag'
# array = Array.new(32) { |i| i * 7 }
# puts TrickBag::BinaryToHexAndAscii.format(array)
# 0x 0 00 07 0E 15 | 1C 23 2A 31 | 38 3F 46 4D | 54 5B 62 69 .....#*18?FMT[bi
# 0x 10 70 77 7E 85 | 8C 93 9A A1 | A8 AF B6 BD | C4 CB D2 D9 pw~.............
It’s at https://github.com/keithrbennett/trick_bag/blob/master/lib/trick_bag/formatters/binary_to_hex_and_ascii.rb. (Or if the link doesn’t work just google “trick_bag github” and go to lib/trick_bag/formatters/binary_to_hex_and_ascii.rb.)
A colleague asked me for something he could use to output binary data to a log file, so I decided to go a little further with this. It outputs data like this:
0x 0 00 07 0E 15 | 1C 23 2A 31 | 38 3F 46 4D | 54 5B 62 69 .....#*18?FMT[bi
0x 10 70 77 7E 85 | 8C 93 9A A1 | A8 AF B6 BD | C4 CB D2 D9 pw~.............
It can be called like this:
require 'trick_bag'
array = Array.new(32) { |i| i * 7 }
puts TrickBag::BinaryToHexAndAscii.format(array)
It’s in my trick_bag gem. That module is at https://github.com/keithrbennett/trick_bag/blob/master/lib/trick_bag/formatters/binary_to_hex_and_ascii.rb.