Last active
September 20, 2015 07:27
-
-
Save lillesvin/f53ed9193210f9818450 to your computer and use it in GitHub Desktop.
Data bending is fun!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
## | |
# ByteScramble | |
# | |
# Copyright 2015 Anders K. Madsen <lillesvin@gmail.com> | |
# Licence: MIT <https://opensource.org/licenses/MIT> | |
# | |
# Description: | |
# ------------ | |
# Takes input from the specified input file or from stdin, | |
# fucks shit up and outputs to stdout. | |
# Works well on JPEGs and is pretty damn awful with PNGs. | |
# | |
# Examples: | |
# --------- | |
# Mess with input.jpg and save to output.jpg: | |
# bytescramble input.jpg > output.jpg | |
# or: | |
# bytescramble < input.jpg > output.jpg | |
# or: | |
# cat input.jpg | bytescramble > output.jpg | |
# | |
# Use a seed, so the same image gets destroyed in the same way every time: | |
# bytescramble -e 1234567890987654321 input.jpg > output.jpg | |
# | |
# Take a screenshot and glitch it up: | |
# import -window root jpeg:- | bytescramble > screenshot.jpg | |
# | |
# Take screenshot, scramble it and set it as wallpaper: | |
# import -window root jpeg:- | bytescramble | feh --bg-fill - | |
# | |
# Take screenshot, fuck it up and lock the screen using the gliched image as | |
# background: | |
# { import -window root jpeg:- | bytescramble | convert - /tmp/lock.png; } && i3lock -i /tmp/lock.png | |
# | |
# For all available options see: | |
# bytescramble --help | |
# | |
# There's technically no reason it shouldn't be able to fuck up non-image data, | |
# I just haven't tried it yet... | |
# | |
require 'optparse' | |
require 'base64' | |
class ByteScramble | |
VERSION = "0.5" | |
attr_reader :file, :bytes, :header, :body, :seed | |
attr_accessor :options, :rng | |
def initialize(options) | |
@options = options | |
if @options[:seed].nil? | |
@rng = Random.new | |
@options[:seed] = @rng.seed | |
else | |
@rng = Random.new(@options[:seed]) | |
end | |
@options[:modifier] = @rng.rand(1..127) unless @options[:modifier] | |
vprint get_options | |
end | |
def fuckup(file) | |
split_input(file) | |
fuckshitup | |
output | |
end | |
def split_input(file) | |
@quads = Base64.encode64(file).scan(/.{1,4}/) | |
@header = @quads[0...headersize(@options[:headersize])] | |
@body = @quads[headersize(@options[:headersize])..-1] | |
end | |
def fuckshitup | |
1.upto(@options[:iterations]) do | |
@body = @body.byte_op(random_range(@body.length), @options[:operator], @options[:modifier], @rng) | |
end | |
end | |
def output | |
out = Base64.decode64([@header + @body].join) | |
if @options[:output] | |
IO.write(@options[:output], out) | |
else | |
print out | |
end | |
end | |
def headersize(size) | |
(size.to_f / 3.to_f).ceil | |
end | |
def get_chunksize | |
@rng.rand(@options[:minchunk]..@options[:maxchunk]) | |
end | |
def random_range(max) | |
chunk = get_chunksize | |
max_offset = max - chunk | |
offset = @rng.rand(0..max_offset) | |
offset..(offset + chunk) | |
end | |
def get_options | |
back = ["bytescramble v%s options:" % ByteScramble::VERSION] | |
@options.each do |k,v| | |
back << " - %-12s: %s" % [k, v] | |
end | |
back.join("\n") + "\n" | |
end | |
def vprint(str) | |
if @options[:verbose] | |
$stderr.print str | |
end | |
end | |
end | |
# Some defaults: | |
options = { | |
:iterations => 10, | |
:maxchunk => 32, | |
:minchunk => 1, | |
:headersize => 128, | |
:operator => :rand | |
} | |
# Parse command line options | |
OptionParser.new do |opts| | |
opts.banner = "Usage: bytescramble [options] [input file]" | |
opts.separator "" | |
opts.separator "Reads the provided file or stdin and fucks up them bytes, y'all!" | |
opts.separator "" | |
opts.separator "Up-fucking options:" | |
opts.on("-e", "--seed INT", Integer, "Seed the PRNG with INT") do |e| | |
options[:seed] = e | |
end | |
opts.on("-p", "--operator TYPE", [:xor, :or, :and, :sub, :mult, :div, :rand], | |
"Operator to use: xor, or, and,", | |
"sub, mult, div, rand (default: %s)" % options[:operator]) do |p| | |
options[:operator] = p | |
end | |
opts.on("-m", "--modifier INT", Integer, "Modifier for operator (default: ", | |
"rand(1..127)). Ignored if -p is 'rand'") do |m| | |
options[:modifier] = m | |
end | |
opts.on("-i", "--iterations INT", Integer, "Scramble INT times (default: %s)" % options[:iterations]) do |i| | |
options[:iterations] = i | |
end | |
opts.on("-C", "--max-chunk INT", Integer, "Max chunk size in bytes (default: %s)" % options[:maxchunk]) do |m| | |
options[:maxchunk] = m | |
end | |
opts.on("-c", "--min-chunk INT", Integer, "Min chink size in bytes (default: %s)" % options[:minchunk]) do |n| | |
options[:minchunk] = n | |
end | |
opts.on("-s", "--header-size INT", Integer, "Do not scramble the first INT bytes", | |
"of the input (default: %s)" % options[:headersize]) do |s| | |
options[:headersize] = s | |
end | |
opts.separator "" | |
opts.separator "Common options:" | |
opts.on_tail("-v", "--verbose", "Print some stuff to stderr") do |v| | |
options[:verbose] = v | |
end | |
opts.on_tail("-V", "--version", "Show version info and exit") do | |
puts "bytescramble v%s" % ByteScramble::VERSION | |
exit | |
end | |
opts.on_tail("-h", "--help", "Show help and exit") do | |
puts opts | |
exit | |
end | |
end.parse! | |
# Extend Array a little | |
class Array | |
def replace(start, array) | |
if (start + array.length) > self.length | |
array = array[0...(self.length - start)] | |
end | |
if start > 0 | |
self[0...start] + array + self[(start + array.length)..-1] | |
else | |
array + self[(start + array.length)..-1] | |
end | |
end | |
def byte_op(range, op, mod, rng) | |
new_range = self[range].map do |x| | |
back = [] | |
Base64.decode64(x).bytes do |b| | |
case op | |
when :xor | |
byte = b ^ mod | |
when :or | |
byte = b | mod | |
when :and | |
byte = b + mod | |
when :add | |
byte = b + mod | |
when :sub | |
byte = b - mod | |
when :mult | |
byte = b * mod | |
when :div | |
byte = b / mod | |
when :rand | |
byte = rng.rand(1..128) | |
end | |
byte = byte % 128 | |
back << byte.chr | |
end | |
Base64.encode64(back.join) | |
end | |
self.replace(range.first, new_range) | |
end | |
end | |
# Run | |
begin | |
bs = ByteScramble.new(options) | |
if ARGV.empty? | |
# Read from stdin | |
ARGF.binmode | |
bs.fuckup(ARGF.read) | |
else | |
bs.fuckup(IO.read(ARGV[0], {:mode => 'rb'})) | |
end | |
rescue Exception => e | |
$stderr.puts "bytescramble: Exception: %s" % e.to_s | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment