[Release] Speed Up your Scripts by Obfuscating them

09/21/2014 18:04 Shadow992#1
Did you ever want to protect your Au3 scripts from being stolen but did you never find a way to do it right? Did you ever struggle with performance issues of AutoIt after (or even without) Obfuscation?

AutoIt is quite slow, so I decided to write a new tool which will actually focus on speeding up your AutoIt Scripts up to a factor of 2! The nice side effect is that these scripts will be hard to read for humans too and it is nearly impossible to get the original source code back.

My tool (named Speedfuscator, which means "Speed-Up"+"Obfuscator") comes with a so called "Starter.exe", you can use this GUI to use my tool without cmd but you can also use cmd to use my script everywhere and with every other script by ease. The command line looks like the following:
Quote:
I "Include-Path" S "Script.au3" oX
Where oX means o1, o2 or o3. o1 will speed up your script just a little bit, o2 will speed it up bit more and o3 tries to get the maximum out of your scripts.

My Script is doing (at o3) 4 simple steps to speed up your AutoIt scripts:
1. It renames all variable names to as short as possible names (Thats why you should avoid Eval/Assign)
2. It removes all unused functions/variables/Lines
3. It inlines functions to the normal script (this is what brings the real big Speed Up)
4. It extracts all Strings to variables. So at the beginning we have to wait a little bit longer but later on the complete script will be executed faster, this is because of the fact that a variable with name $abc is much shorter than this String: "Please enter your Name: ".

I want to add even more thing to make Speed Up better, but for the moment i will release this version so that all can test and help me finding bugs.

I also provide some Examples (and Example-Times):

Quote:
Some "real World" examples:

If we use Scripts where my Tool can bring up quite a good Speed Up, it may look like that:
As you easily can see Speed-Up gets better the more functions are called and the less complicated they are. So normal Speed-Up will be "only" around 1.1 to 1.2. If you have got some function heavy Scripts it may get up to 1.5 and if you use some "crazy" scripts you may also get factor of 2 (but these scripts are rarely useful in real world).

If you find some problems, just let me know. I will have a look at it.
09/21/2014 18:13 snow#2
#moved
09/21/2014 18:15 Shadow992#3
Quote:
Originally Posted by snow View Post
#moved
Eigentlich habe ich es mit Absicht in die AutoIt-Section gestellt, weil es wirklich nur den AutoIt-Leuten etwas bringt, andere Leute interessieren sich dafür recht wenig und die meisten AutoIt-Leute werden sich auch öfter in der AutoIt-Section rumtreiben als hier. :D

Aber prinzipiell ists mir egal. :D
09/21/2014 19:20 alpines#4
Da das ja scheinbar weiterentwickelt wird will ich mal einen kleinen Ansatz mitgeben:

Prüfe ob ein String mehrmals oder nur einmal vorkommt,denn so wie ich das im Beispiel sehe, kommt "Time taken: " und " ms" nur einmal vor. Dafür Speicher zu reservieren ist meiner Meinung nach Schwachsinn.
09/21/2014 19:57 Shadow992#5
Quote:
Originally Posted by alpines View Post
Da das ja scheinbar weiterentwickelt wird will ich mal einen kleinen Ansatz mitgeben:

Prüfe ob ein String mehrmals oder nur einmal vorkommt,denn so wie ich das im Beispiel sehe, kommt "Time taken: " und " ms" nur einmal vor. Dafür Speicher zu reservieren ist meiner Meinung nach Schwachsinn.
Möchte man meinen, aber es geht hier wirklich nur um die Geschwindigkeit und AutoIt arbeitet intern schneller mit Variablen als mit Strings, das heißt selbst ein $langer_variablen_name wird schneller verarbeitet als ein "Test". Dafür hat man halt ganz am Anfang etwas längere Startzeit, da am Anfang aber meistens eh nur die GUI erstellt wird, stört das nicht viel.

Edit:
Aber auf der anderen Seite, wenn das auch nicht in einer Schleife vorkommt und auch nicht in einer Funktion, könnte man es sich tatsächlich sparen. Werd ich berücksichtigen für die nächste Version, Danke. :)
09/21/2014 22:10 YatoDev#6
versuch mal rechenoperationen mit konstanten schon vorher komplett zu errechnen.
du könntest $a = $a + 1 zu $a += 1 optimieren.
entferne alle dim.
Bit operationen kann man eigentlich immer schon vorher auflösen.
Strings kann man versuchen möglichst früh zusammenzusetzen.
bei if abfragen true und false durch 1 und 0 ersetzen.
$a = $b * 2 durch $a = $b + $b ersetzen da normalerweise + schneller ausgeführt wird(könnte sein)
wird das const keyword eigentlich dringend gebraucht? könnte man weglassen
experimentell könnte man includes ohne error handling und anderen verlangsamerungen anbieten:D

alles nur ideen. kann sein das manches schwachsinn ist^^
09/21/2014 22:25 alpines#7
Ich weiß leider nicht wie der AutoIt-Interpreter das handlet aber der Zugriff auf Const-Variablen ist im Allgemeinen ein wenig schneller als bei variablen Variablen (lol).
09/21/2014 22:30 Shadow992#8
Quote:
Originally Posted by »FlutterShy™ View Post
versuch mal rechenoperationen mit konstanten schon vorher komplett zu errechnen.
Da dieses Projekt nur der Einstieg in einen eigenen erweiterten (Multithreading etc.) AutoIt-Interpreter sein soll, werden genau solche Sachen auch später Einzug in dieses projekt finden, die Grundsteine dafür sind schon gelegt. ;)
Ich will den Speedfuscator sogar soweit bringen, dass er automatisch Variablen, deren Wert zu einem bestimmten Zeitpunkt fest ist soweit ausrechnet, dass er die konkrete Zahl einsetzen kann.
Quote:
Originally Posted by »FlutterShy™ View Post
du könntest $a = $a + 1 zu $a += 1 optimieren.
Viel zu wenig Einsparungspotential.

Quote:
Originally Posted by »FlutterShy™ View Post
entferne alle dim.
Versteh ich nicht? Es werden alle unbenutzten variablen inklusive unnötige Global/Dims/usw. entfernt. Dass momentan manchmal noch ein paar übrig bleiben hat einen einfachen Grund. Meisten sind auf der rechten Seite, dann Befehle. Da mein Tool aber noch keine Befehle interpretieren kann, lässt er sie lieber erst einmal unberührt und deswegen werden solche Variablen dann auch drin gelassen inklusive dim.
Quote:
Originally Posted by »FlutterShy™ View Post
Bit operationen kann man eigentlich immer schon vorher auflösen.
Strings kann man versuchen möglichst früh zusammenzusetzen.
Wie oben angedeutet wird das alles definitiv kommen und die Grundzüge sind ja schon drin, wenn auch nicht sichtbar. :D

Quote:
Originally Posted by »FlutterShy™ View Post
bei if abfragen 1 und 0 durch true und false ersetzen.
$a = $b * 2 durch $a = $b + $b ersetzen da normalerweise + schneller ausgeführt wird(könnte sein)
Hab ich mir auch schon überlegt, bringt aber effektiv gesehen nichts, weil der Parser ungefähr 10000x so lange zum parsen des Zeichens braucht als das Berechnen selbst in Anspruch nimmt, daher ist das nicht sinnvoll soetwas zu ändern.

Quote:
Originally Posted by »FlutterShy™ View Post
wird das const keyword eigentlich dringend gebraucht? könnte man weglassen
experimentell könnte man includes ohne error handling und anderen verlangsamerungen anbieten:D
Nette Idee ist aber an der falschen Stelle gespart. Das Const-Keyword wird effektiv von AutoIt nicht genutzt, ist also nur dafür da um dem Programmierer vor sich selbst zu schützen.
Quote:
Originally Posted by »FlutterShy™ View Post
alles nur ideen. kann sein das manches schwachsinn ist^^
Ach was :D
Nur her mit den Ideen. :D
09/21/2014 22:51 YatoDev#9
Quote:
Originally Posted by Shadow992 View Post
Da dieses Projekt nur der Einstieg in einen eigenen erweiterten (Multithreading etc.) AutoIt-Interpreter sein soll, werden genau solche Sachen auch später Einzug in dieses projekt finden, die Grundsteine dafür sind schon gelegt. ;)
Ich versteh auch immer noch nicht warum die autoit entwickler so wenig optimieren und sowas.
Solltest du besser machen:D

QuickIt wär auch ein netter name:D

Quote:
Originally Posted by alpines View Post
Ich weiß leider nicht wie der AutoIt-Interpreter das handlet aber der Zugriff auf Const-Variablen ist im Allgemeinen ein wenig schneller als bei variablen Variablen (lol).
der compiler achtet vorher wahrscheinlich auf solche statements um tu überwachen das es im script nicht überschrieben wird.
Der autoit interpret ist ein mysterium:D
09/21/2014 23:02 alpines#10
Quote:
Originally Posted by »FlutterShy™ View Post
QuickIt wär auch ein netter name:D
Och nee, dann doch lieber gleich AutoThat.
09/21/2014 23:04 Shadow992#11
Quote:
Originally Posted by »FlutterShy™ View Post
Ich versteh auch immer noch nicht warum die autoit entwickler so wenig optimieren und sowas.
Solltest du besser machen:D

QuickIt wär auch ein netter name:D
Dachte bisher an "WAAL" = Windows Advanced Automation Language.
Aber noch ists nicht so weit, wird sich noch etwas hinziehen. :D
Was die Optimierung von AutoIt angeht, kann man echt viel mehr rausholen, aber das Parsen ansich läuft echt ziemlich gut/schnell.
Quote:
Originally Posted by »FlutterShy™ View Post
der compiler achtet vorher wahrscheinlich auf solche statements um tu überwachen das es im script nicht überschrieben wird.
Der autoit interpret ist ein mysterium:D
Da hast du alledrings wirklich Recht, bei der Recherche, welche Optimierungsmöglichkeiten es gibt (also auf gut deutsch beim Ausprobieren :D). habe ich festgestellt, dass je nachdem wie viele Variable deklariert sind (also wie viele verschiedene es gibt) der Interpreter mal schneller, mal langsamer läuft. Ist auf den ersten Blick nicht verwunderlich, je weniger variablen desto schneller und das stimmt auch, aber scheinbar gibt es bei AutoIt ein paar magische Variablen-Anzahl, sodass das Skript bis zu 10% schneller läuft, konnte aber nicht rekontruieren wieso das passiert (ich vermute es liegt am Caching und Alignment). Hätte es zu gerne in mein Tool eingebaut, aber joar, dann halt net. :D
09/21/2014 23:50 ​Tension#12
Hust ich will auch was dazu schreiben D:
wie der Interpreter das ganze genau verwaltet weiß ich nicht, nur wie er den Source/Bytecode liest.

Operatoren wie + - * / usw. zu += -= *= /= usw. zu ändern (falls möglich) ist Sinnvoll.
Code:
$yolo += 1
ist viel kürzer als
Code:
$yolo = $yolo + 1
Es ist genauso wie bei ASM:
Code:
add yolo, 1 //oder inc yolo
ist kürzer als
Code:
mov edx, yolo
add edx, 1
mov yolo, edx
Zumal der Interpreter bei AutoIt bei Variante 2 den String "yolo" doppelt einlesen müsste und überprüfen müsste welchen Wert die Variable überhaupt hat.

Selbes gilt für:
Code:
$swag = $yolo + $yolo
$swag = $yolo * 2
Man müsste den String einfach doppelt laden was unnötig ist.

Und außerdem auch für:
Code:
$lol = false
if $lol = true Or $lol = 0 Then
...
( Nur ein Beispiel )
wenn man false/true benutzt werden diese als String gespeichert, 0/1 im Gegensatz nicht.

Um AutoIt schneller zu machen muss man einfach nur kürzen sprich:
Kurze TEXTE also kurze Variablen, Funktionen, Makros , true/false zu 0/1 ändern, etc.
Auf %swag = %swag + 1 verzichten und += *= /= etc. verwenden.
Im allgemeinen gilt einfach so wenig Text wie möglich damit das Script schneller läuft.

Und wenn es möglich ist in AutoIt viel Code in eine Zeile zu klatschen und du es noch schneller haben willst dann sparst du am besten an Zeilen weil ein Umbruch auch noch einen Bytecode hat der gelesen werden muss :p

Aber ich vermute das dir das sowieso bewusst ist, wollte es nur noch mal erklären.

P.S: hatte nun doch endlich mal Zeit deine CrackMe anzugucken und hab alles dafür fertig, ein Update dauert jedoch noch da ich ShatterIt optimieren möchte und es deshalb neu schreibe.
09/21/2014 23:59 YatoDev#13
Mit dem namen meinte ich eher diesen optimierer und nicht den neuen interpret:D

Edit mir fällt grad ein wass ich mit dem entfernen von dim meinte.
Der autoit interpret "ignoriert" global und dim auserhalb von funktionen.
also kann man diese weglassen. das macht den code kürzer und der interpret macht ja eh das was er will.

Man kann vermuten das bei global und dim (wenn man es weglässt) weniger verarbeitet werden muss und der interpret einfach die standard initialisierung macht
09/22/2014 01:11 Tasiro#14
Inlining findet bei Funktionen, welche ein String-Literal zurückgeben, nicht statt.
"Local $a = 1" führt zu "Local", eine leere Datei zu einem Fehler.
Der Bedingungsoperator scheint noch nicht implementiert worden zu sein.
"Execute" sollte Optimierung von sich aus einschränken, sofern erreichbar.


Wie viel Datenflussanalyse betreibst du denn? Ist Konstantenpropagation dann als nächste Optimierung dran?
09/22/2014 09:07 Shadow992#15
Quote:
Originally Posted by ​Tension View Post
Hust ich will auch was dazu schreiben D:
wie der Interpreter das ganze genau verwaltet weiß ich nicht, nur wie er den Source/Bytecode liest.

Operatoren wie + - * / usw. zu += -= *= /= usw. zu ändern (falls möglich) ist Sinnvoll.
Code:
$yolo += 1
ist viel kürzer als
Code:
$yolo = $yolo + 1
Es ist genauso wie bei ASM:
Code:
add yolo, 1 //oder inc yolo
ist kürzer als
Code:
mov edx, yolo
add edx, 1
mov yolo, edx
Zumal der Interpreter bei AutoIt bei Variante 2 den String "yolo" doppelt einlesen müsste und überprüfen müsste welchen Wert die Variable überhaupt hat.

Selbes gilt für:
Code:
$swag = $yolo + $yolo
$swag = $yolo * 2
Man müsste den String einfach doppelt laden was unnötig ist.

Und außerdem auch für:
Code:
$lol = false
if $lol = true Or $lol = 0 Then
...
( Nur ein Beispiel )
wenn man false/true benutzt werden diese als String gespeichert, 0/1 im Gegensatz nicht.

Um AutoIt schneller zu machen muss man einfach nur kürzen sprich:
Kurze TEXTE also kurze Variablen, Funktionen, Makros , true/false zu 0/1 ändern, etc.
Auf %swag = %swag + 1 verzichten und += *= /= etc. verwenden.
Im allgemeinen gilt einfach so wenig Text wie möglich damit das Script schneller läuft.

Und wenn es möglich ist in AutoIt viel Code in eine Zeile zu klatschen und du es noch schneller haben willst dann sparst du am besten an Zeilen weil ein Umbruch auch noch einen Bytecode hat der gelesen werden muss :p

Aber ich vermute das dir das sowieso bewusst ist, wollte es nur noch mal erklären.

P.S: hatte nun doch endlich mal Zeit deine CrackMe anzugucken und hab alles dafür fertig, ein Update dauert jedoch noch da ich ShatterIt optimieren möchte und es deshalb neu schreibe.
Ich meinte damit, dass kaum jemand freiwillig "$a=$a+1" schreibt und deswegen nur sehr wenige solche Optimierungen pro Skript ausgeführt werden. Damit sehe ich Preis/Leistung nicht optimal an und will erst einmal die vielversprechenderen Sachen einbauen, was irgendwie sicher öfter irgendwo gebraucht wird.
Quote:
Originally Posted by »FlutterShy™ View Post
Mit dem namen meinte ich eher diesen optimierer und nicht den neuen interpret:D

Edit mir fällt grad ein wass ich mit dem entfernen von dim meinte.
Der autoit interpret "ignoriert" global und dim auserhalb von funktionen.
also kann man diese weglassen. das macht den code kürzer und der interpret macht ja eh das was er will.

Man kann vermuten das bei global und dim (wenn man es weglässt) weniger verarbeitet werden muss und der interpret einfach die standard initialisierung macht
Ahso ja das wäre eine Idee. :D
Quote:
Originally Posted by Tasiro View Post
Inlining findet bei Funktionen, welche ein String-Literal zurückgeben, nicht statt.
"Local $a = 1" führt zu "Local", eine leere Datei zu einem Fehler.
Der Bedingungsoperator scheint noch nicht implementiert worden zu sein.
"Execute" sollte Optimierung von sich aus einschränken, sofern erreichbar.


Wie viel Datenflussanalyse betreibst du denn? Ist Konstantenpropagation dann als nächste Optimierung dran?
Naja, da ich den Parser als Grundgerüst für einen späteren Compiler/interpreter nehmen möchte, betreibe ich sehr viel Analyse, was Source angeht. Momentanbesteht die Analyse nur aus das exakte Parsen (inklusive Fehler Warnung bei falschem AutoIt-Code, wobei das bisher nur einfache Fehler erkennt) des Codes wie der original Parser, später soll aber sehr wohl eine umfangreiche Analyse ausgeführt werden. Das Ganze soll sogar soweit gehen, dass der Parser das Skript intern simuliert (also ohne GUI und sonstigen Schnick-Schnack versteht sich) und dann alle Variablen, die ganz sicher immer denselben Wert an dieser Stelle besitzen, durch deren Wert ersetzt. Da ist es dann egal ob Const davor steht oder nicht, das Ganze soll allgemein klappen.
Was du mit "Funktionen mit Strings als Return werden nicht inlined" meinst, ist mir nicht klar. Sie werden noch nicht optimal inlined, wie man an meinem Beispiel im Folgenden sehen kann, aber das wird sich ja dann durch die umfangreiche Analyse später von selbst erledigen.
Auch den Fehler mit dem Local "$a=$a+1" konnte ich nicht reproduzieren, das muss also an etwas anderes in deiner Funktion liegen, wäre super wenn du sie mir einmal zukommen lässt. :D

Beispiel:
PHP Code:
Msgbox(0,test(),test())
func test()
    
Local $a=1
    
return "hey"&$a
endfunc 
Wird zu:

PHP Code:
$b "hey"
do
Local $c 1
$d 
$b $c
ExitLoop 1
Until 1 
== 1
do
Local $e 1
$f 
$b $e
ExitLoop 1
Until 1 
== 1
Msgbox 
$d $f 
Edit:
Jop ternäre Operatoren werden bisher nur extrem rudimentär unterstützt, wusste bis vor nem Tag noch nicht einmal, dass es die jetzt seit 2 Versionen oder so gibt. :D Werd ich aber noch einbauen.