Die Artikel HowToObfuscate und HowToObfuscate2 überschneiden sich inhaltlich. Bitte hilf mit, das zu verbessern, indem du die Artikel unter einem Namen zusammenführst.
Bitte entferne anschließend den Redundanz-Baustein.
Vorgeschichte
Die hier erläuterte Technik wurde von mir (murphy) benutzt, um das Witz-Script Chunky-Bacon-Server (Version ohne Port 203) zu erstellen, das es sogar auf RedHanded geschafft hat; die Idee ist nicht neu, aber immer wieder witzig.
Begriffe
Obfuscating (Vernebeln, Verschleiern) bedeutet, einen Code lauffähig, aber für Menschen unlesbar zu machen. ASCII-Art ist der Versuch, nur mit den grundlegendsten Schriftzeichen Grafiken zu basteln; sogar Star Wars kann man damit herstellen.
Zurück zum Eigentlichen:
Wie kann ich mein Script in ein ASCII-Kunstwerk verwandeln?
Fragen hierzu am besten im Ruby-Forum stellen.
Du brauchst:
- das Script
- die Grafik (zB Schwarz-Weiß-PNG)
- RMagick
- rb2art:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
# murphy's rb2art
# version 2005.august.11
# Licence: LGPL
if ARGV.empty?
puts <<-INFO
Usage: ruby rb2art.rb <script> <graphic>
INFO
exit
end
require 'RMagick'
graphic = Magick::ImageList.new ARGV.pop
c, r = graphic.columns, graphic.rows
graphic.resize! c * 0.8, r * 0.5
c, r = graphic.columns, graphic.rows
a = graphic.export_pixels 0, 0, c, r, 'I'
colors = '##### '
require 'enumerator'
ascii = ''
a.each_slice(c) do |row|
ascii << row.map { |x| colors[x / 16,1] }.join << "\n"
end
program = File.read ARGV.pop
module AsciiMask
ENCODE = {
"\s" => '=s',
"\t" => '=t',
"\n" => '=n',
"\r" => '=r',
"=" => '==',
"#" => '=#',
}
DECODE = ENCODE.invert
def encode s
s.gsub!(/[\s#=]/) { |c| ENCODE[c] }
end
def decode s
s.gsub!(/=./m) { |c| DECODE[c] }
end
end
include AsciiMask
program = encode program
i = -1
art = ascii.split('').map do |a|
if a == '#'
if i + 1 < program.size
program[i += 1,1]
else
'#'
end
else
a
end
end.join
$stderr.puts 'Warning: Graphic is too small for script!' if i + 1 != program.size
puts <<-'ART' % art
eval <<-'RUBY'.gsub(/\s/,'').gsub(/=./){|c|{'=s'=>"\s",'=t'=>"\t",'=n'=>"\n",'=r'=>"\r",'=='=>'=','=#' => '#'}[c]}
%sRUBY
ART |
Beispiel
- Speichere folgendes Script unter small.rb:
1
2
3
|
# A small program
puts 'Hello ASCII-art world!' |
- Lade das Rails-Icon herunter und speichere es in rails.ico.
- Kopiere alles zusammen mit rb2art.rb in einen Ordner.
- Starte es, zum Beispiel mit:
ruby -rubygems rb2art.rb small.rb rails.ico > small.obf.rb
Ergebnis:
1
2
3
4
5
6
7
8
9
10
|
eval <<-'RUBY'.gsub(/\s/,'').gsub(/=./){|c|{'=s'=>"\s",'=t'=>"\t",'=n'=>"\n",'=r'=>"\r",'=='=>'=','=#' => '#'}[c]}
=#=sA =ss
mal l=
sp rog
ra m=n=np
ut s=s'He
ll o=sASC
II -art=s
wor ld!'=n#
RUBY |
Obwohl das jetzt keiner mehr lesen kann, ist es ausführbar und tut dasselbe wie das Originalscript:
% ruby small.obf.rb
Hello ASCII-art world!
Anpassen
Bildgröße
In der Zeile
|
graphic.resize! c * 0.8, r * 0.5 |
wird das Bild verkleinert und ein wenig platt gedrückt; da Buchstaben normalerweise höher als weit sind, wirkt das Bild am Schluss wieder normal.
Wenn man die Faktoren anpasst, kann man das Bild natürlich vergrößern und verkleinern, bis es zum Code passt.
Dunkelheit
Die Zeile
steuert, wie die Grauwerte des Bildes übersetzt werden: Links = schwarz bis rechts = weiß.
Mit
|
colors = '############## ' |
würde das Bild dunkler, die Linien breiter, und du hast mehr Platz für Code.
Mit
bewirkst du das Gegenteil.
Erläuterung
Bild --> ASCII-Grafik
Das Bild wird also gelesen:
1
2
|
require 'RMagick'
graphic = Magick::ImageList.new ARGV.pop |
und in die richtige Größe umgewandelt:
1
2
|
c, r = graphic.columns, graphic.rows
graphic.resize! c * 0.8, r * 0.5 |
Dann wird es in ein Array von Grauwerten (mit 'I') konvertiert:
1
2
|
c, r = graphic.columns, graphic.rows
a = graphic.export_pixels 0, 0, c, r, 'I' |
Näheres unter http://www.simplesystems.org/RMagick/doc/usage.html.
Als nächstes werden die Grauwerte in Zeichen umgewandelt, genauer in ' ' (Leerstelle) und '#'.
Leider sind die Grauwerte in einem eindimensionalen Array gespeichert, Pixel an Pixel. Wir brauchen ein zweidimensionales Array, also ein Array von Arrays:
1
2
3
4
5
6
|
colors = '##### '
require 'enumerator' # weil uns each_slice eine Menge Arbeite abnimmt
ascii = ''
a.each_slice(c) do |row| # jetzt haben wir jede Reihe einzeln
ascii << row.map { |x| colors[x / 16,1] }.join << "\n"
end |
Mehr zu each_slice unter Undokumentierte_Methoden.
Jetzt soll jedes '#' in der Grafik durch ein Zeichen des Scripts ersetzt werden.
ASCII maskieren und demaskieren
Nachdem wir die Grafik im String ascii haben, der so aussieht:
1
2
3
4
5
6
7
8
9
|
"##### ###
### ##
## ###
## ######
## ######
## ######
## ######
### #######
" |
müssen wir noch das Script ein wenig umformen; speziell dürfen die Zeichen '#', ' ' sowie Zeilenwechsel und jede andere Art Whitespace nicht verloren gehen. Das Modul AsciiMask übernimmt diese Funktion durch einen simplen Abbildungs-Hash (man könnte es mapping nennen):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
module AsciiMask
ENCODE = {
"\s" => '=s',
"\t" => '=t',
"\n" => '=n',
"\r" => '=r',
"=" => '==',
"#" => '=#',
}
DECODE = ENCODE.invert
def encode s
s.gsub!(/[\s#=]/) { |c| ENCODE[c] }
end
def decode s
s.gsub!(/=./m) { |c| DECODE[c] }
end
end |
Der Vorteil dieser Codierung ist, dass der ursprüngliche Text auch wiederhergestellt werden kann, wenn man beliebig '#' und Whitespace einfügt. Einzige Ausnahme: '##' würde zu '#', aber das ist kein Problem, da dies in Ruby nur einen Kommentar einleitet.
AsciiMask.decode ist netterweise mit drin, wird aber nicht gebraucht.
1
2
|
include AsciiMask
program = encode program |
Das Programm wird verschlüsselt. Nun muss es nur noch "eingewoben" werden.
Script in die Grafik einweben
Die Grafik wird durchlaufen, um jedes Zeichen durch das nächste (i-te) Zeichen im maskierten Script zu ersetzen. Ist das Script vorzeitig zuende, wird eine Warnung ausgegeben:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
i = -1
art = ascii.split('').map do |a|
if a == '#'
if i + 1 < program.size
program[i += 1,1]
else
'#'
end
else
a
end
end.join
$stderr.puts 'Warning: Graphic is too small for script!' if i + 1 != program.size |
Ausgabe
Zuletzt wird das fertige unlesbare Script ausgegeben:
1
2
3
4
|
puts <<-'ART' % art
eval <<-'RUBY'.gsub(/\s/,'').gsub(/=./){|c|{'=s'=>"\s",'=t'=>"\t",'=n'=>"\n",'=r'=>"\r",'=='=>'=','=#' => '#'}[c]}
%sRUBY
ART |
Es besteht einfach aus einer eval-Anweisung, einem gekürzten AsciiMask.decode und der ASCII-Grafik in einem Heredoc.
Viel Spaß damit!