Jump to content

ORfuscator


czardas
 Share

Recommended Posts

ORfuscate your script. I'm not sure if this exists, but it is intended as a pre-obfuscation tool. It might be a new invention! While it does not guarantee any protection by itself, ORfuscation does offer some benefit in certain situations. It's more useful in cases where knowing the original numbers is essential to fully understand a script. In other cases it's just adds a slight inconvenience to the reverse engineer due to superflous internal routines.

You may wish to convert globals to magic numbers before ORfuscation. I believe PreExpand by guinness will do this. Additional features such as creating fake constants may be included in a future release. Please do not post ORfuscated scripts on the forum. You may post them in this thread if it is relevant to the topic or provides an interesting example. Please double check to make sure that your scripts are still functional after ORfuscation.

The example at the end of the script (lines 236 onwards) should be removed before testing with your own script.

;

; #FUNCTION# ====================================================================================================================
; Name...........: ORfuscate
; Description ...: Refactorization of magic numbers, 32-bit integers and hard coded BitOR() arguments.
; Syntax.........: ORfuscate($sScript)
; Parameters ....: $sScript - String input of an AutoIt script.
; Return values .: Success  - Returns the orfuscated script.
;                  Failure  - Returns the original script and sets @error to the following values:
;                  |@error = 1 : Script contains no data.
;                  |@error = 2 : No suitable separators found (this should not happen).
; Author ........: czardas
; Modified.......:
; Remarks .......: This is an experimental project with very little testing - so use at your own risk.
; Link ..........:
; Example .......:
; ===============================================================================================================================

Func ORfuscate($sScript)
    If $sScript = "" Then Return SetError(1, 0, "")

    ; Get suitable padding to separate strings, comments and directives from the AutoIt.
    Local $iCount = 63743 ; BMP Private Range Upper Limit

    $sScript &= @LF

    Local $sIgnoreOn  = " " & __GetSubstitute($sScript, $iCount) & " " ; Spaces are used for extra padding.
    If @error Then Return SetError(2, 0, $sScript)
    Local $sIgnoreOff = " " & __GetSubstitute($sScript, $iCount) & " "
    If @error Then Return SetError(2, 0, $sScript)

    Local $bIsAutoIt = True, _ ; The current position is outside strings, comments and directives.
    $sNotAutoIt = "", _ ; Strings, comments or directives to avoid.
    $sExpected = "", _ ; Regexp pattern to match the termination of a string, comment or directive.
    $sNextChar, $sNewAutoIt = ""

    ; Identify strings, comments and directives.
    For $i = 1 To StringLen($sScript)
        $sNextChar = StringMid($sScript, $i, 1)
        If $bIsAutoIt Then
            If StringRegExp($sNextChar, '[#''";]') Then
                $sNewAutoIt &= $sIgnoreOn & $sNextChar
                $bIsAutoIt = False

                Switch $sNextChar
                    Case "#"
                        $sExpected = StringRegExp(StringMid($sScript, $i, 4), "(?:)(#cs\b)") ? "(?:)([\r\n]\h*#ce\b.*\v)" : "\v"
                    Case "'"
                        $sExpected = "'"
                    Case """"
                        $sExpected = """"
                    Case ";"
                        $sExpected = "\v"
                EndSwitch
            Else
                $sNewAutoIt &= $sNextChar
            EndIf
        Else
            $sNotAutoIt &= $sNextChar
            If StringRegExp($sNotAutoIt, $sExpected) Then
                $sNewAutoIt &= $sNotAutoIt & $sIgnoreOff
                $bIsAutoIt = True
                $sExpected = ""
                $sNotAutoIt = ""
            EndIf
        EndIf
    Next

    ; Split the script to reveal 32-bit integers, Bitwise-OR'ed integers and also capture the rest of the code.
    Local $aAutoIt = StringRegExp($sNewAutoIt, "(?i)(?s)(?U)(" & $sIgnoreOn & ".+" & $sIgnoreOff & ")|\b\d*\.\d+\b|\b\d*\.?\d+e[\+\-]?\d+\b|(?<=[\s=,/\Q[(+-*^?:\E])(\d+|0x[A-F0-9]+|BitOR\h*\([x\h0-9,A-F]+\))(?=[\s=,/\Q)]+-*^?:\E])|\$*\b\w+\b|.", 3)

    ; Search for integers
    Local $iBound = UBound($aAutoIt), $iInt32, $sIdx = ""
    For $i = 0 To $iBound -1
        $iInt32 = ""
        If StringIsInt($aAutoIt[$i]) Or StringRegExp($aAutoIt[$i], "(?i)(\A0x[A-F\d]{1,8}\z)") Or StringRegExp($aAutoIt[$i], "(?i)(BitOR\h*\([x\h\d,A-F]+\))") Then $iInt32 = Execute($aAutoIt[$i])
        If VarGetType ($iInt32) <> "Int32" Then ContinueLoop
        $aAutoIt[$i] = $iInt32
        $sIdx &= $i & ","
    Next

    If Not $sIdx Then Return SetExtended(1, $sScript) ; No changes can be made

    Local $aIdx = StringSplit(StringTrimRight($sIdx, 1), ",", 2)
    Local $iBoundIdx = Ubound($aIdx)

    Local $aSplit, $aMagic[$iBoundIdx][2][3]
    For $i = 0 to $iBoundIdx -1
        $aSplit = MagicSplit($aAutoIt[$aIdx[$i]])
        $aMagic[$i][0][0] = $aSplit[0] ; 1st int
        $aMagic[$i][1][0] = $aSplit[1] ; 2nd int

        $aSplit = MagicSplit($aMagic[$i][0][0])
        $aMagic[$i][0][1] = $aSplit[0] ; 1st int - 1st element
        $aMagic[$i][0][2] = $aSplit[1] ; 1st int - 2nd element

        $aSplit = MagicSplit($aMagic[$i][1][0])
        $aMagic[$i][1][1] = $aSplit[0] ; 2nd int - 1st element
        $aMagic[$i][1][2] = $aSplit[1] ; 2nd int - 2nd element
    Next

    ;_WalkThrough3D($aMagic, "zx")

    Local $sCheckDupes
    For $i = 0 to $iBoundIdx -1
        If $aMagic[$i][0][0] = 0 Then
            If $aMagic[$i][1][1] = 0 Or $aMagic[$i][1][2] = 0 Then
                $aAutoIt[$aIdx[$i]] = MagicFormat($aAutoIt[$aIdx[$i]])
            Else
                $aAutoIt[$aIdx[$i]] = "BitOR(" & MagicFormat($aMagic[$i][1][1]) & ", " & MagicFormat($aMagic[$i][1][2]) & ")"
            EndIf

        ElseIf $aMagic[$i][1][0] = 0 Then
            If $aMagic[$i][0][1] = 0 Or $aMagic[$i][0][2] = 0 Then
                $aAutoIt[$aIdx[$i]] = MagicFormat($aAutoIt[$aIdx[$i]])
            Else
                $aAutoIt[$aIdx[$i]] = "BitOR(" & MagicFormat($aMagic[$i][0][1]) & ", " & MagicFormat($aMagic[$i][0][2]) & ")"
            EndIf

        ElseIf Random(0, 1, 1) Then
            $aAutoIt[$aIdx[$i]] = "BitOR("
            $sCheckDupes = "|"
            For $j = 0 To 1
                For $k = 1 To 2
                    If $aMagic[$i][$j][$k] <> 0 And Not StringInStr($sCheckDupes, "|" & $aMagic[$i][$j][$k] & "|") Then
                        $aAutoIt[$aIdx[$i]] &= MagicFormat($aMagic[$i][$j][$k]) & ", "
                        $sCheckDupes &= $aMagic[$i][$j][$k] & "|"
                    EndIf
                Next
            Next
            $aAutoIt[$aIdx[$i]] = StringTrimRight($aAutoIt[$aIdx[$i]], 2) & ")"

        ElseIf $aMagic[$i][0][0] <> $aMagic[$i][1][0] Then
            $aAutoIt[$aIdx[$i]] = "BitOR(" & MagicFormat($aMagic[$i][0][0]) & ", " & MagicFormat($aMagic[$i][1][0]) & ")"

        Else
            $aAutoIt[$aIdx[$i]] = MagicFormat($aAutoIt[$aIdx[$i]])
        EndIf
    Next

    $sScript = ""
    For $i = 0 To $iBound -1
        $sScript &= $aAutoIt[$i]
    Next

    $sScript = StringReplace($sScript, $sIgnoreOn, "")
    $sScript = StringReplace($sScript, $sIgnoreOff, "")
    $sScript = StringTrimRight($sScript, 1) ; Remove LF added at the start

    Return $sScript
EndFunc

Func XORSplit($i32Bit) ; Splits an integer into two randomly selected bitwise exclusive integers.
    If Not IsInt($i32Bit) Or $i32Bit >= 2^32 Or $i32Bit < 0x80000000 Then Return SetError(1, 0, 0)

    Local $aXOR[2]
    $aXOR[0] = 0
    ; Randomly select which bits to reproduce in the first return value. => $aXOR[0]
    Local $iBitPos
    For $i = 0 To 31
        $iBitPos = 2^$i
        If BitAND($i32Bit, $iBitPos) = $iBitPos And Random(0, 1, 1) Then $aXOR[0] = BitOR($aXOR[0], $iBitPos)
    Next
    ; Check the sign bit.
    If BitAND($i32Bit, 0x80000000) = 0x80000000 And Random(0, 1, 1) Then $aXOR[0] = BitOR($aXOR[0], 0x80000000)

    ; Exclude the bits set to one (in the first return value) from the original input.
    $aXOR[1] = BitXOR($i32Bit, $aXOR[0])
    Return $aXOR ; Array values can be Bitwise-OR'ed together to reproduce the original input.
EndFunc ;==> XORSplit

Func ORSplit($i32Bit) ; Splits an integer into two randomly selected bitwise non-exclusive integers.
    If Not IsInt($i32Bit) Or $i32Bit >= 2^32 Or $i32Bit < 0x80000000 Then Return SetError(1, 0, 0)

    Local $aOR[2]
    $aOR[0] = 0
    $aOR[1] = 0

    ; Randomly select which bits to reproduce in either one or both return values.
    Local $iBitPos, $iSwitch = Random(1,3,1)
    For $i = 0 To 31
        $iBitPos = 2^$i
        If BitAND($i32Bit, $iBitPos) = $iBitPos Then
            For $n = 1 To 2
                If BitAND($iSwitch, $n) = $n Then $aOR[$n -1] = BitOR($aOR[$n -1], $iBitPos)
            Next
            $iSwitch = Random(1,3,1) ; Set the switch for the next run.
        EndIf
    Next

    ; Check the sign bit.
    If BitAND($i32Bit, 0x80000000) = 0x80000000 Then
        For $n = 1 To 2
            If BitAND($iSwitch, $n) = $n Then $aOR[$n -1] = BitOR($aOR[$n -1], 0x80000000)
        Next
    EndIf

    Return $aOR ; Array values can be Bitwise-OR'ed together to reproduce the original input.
EndFunc ;==> ORSplit

Func MagicFormat($iInt)
    Return '0x' & StringRegExpReplace(Hex($iInt), '\A0{0,'& 2*Random(2,3,1) &'}', '')
EndFunc

Func MagicSplit($iInt)
    Local $aSplit = ORSplit($iInt)
    If $aSplit[0] = $iInt Or $aSplit[1] = $iInt Or $aSplit[0] = 1 Or $aSplit[1] = 1  Then $aSplit = XORSplit($iInt)
    Return $aSplit
EndFunc

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __GetSubstitute
; Description ...: Searches for a character to be used for substitution, ie one not contained within the input string
; Syntax.........: __GetSubstitute($string, ByRef $iCountdown)
; Parameters ....: $string   - The string of characters to avoid
;                  $iCountdown - The first code point to begin checking
; Return values .: Success   - Returns a suitable substitution character not found within the first parameter
;                  Failure   - Sets @error to 1 => No substitution character available
; Author ........: czardas
; Comments ......; This function is connected to the function _CSVSplit and was not intended for general use
;                  $iCountdown is returned ByRef to avoid selecting the same character on subsequent calls to this function
;                  Initially $iCountown should be passed with a value = 63743
; ===============================================================================================================================

Func __GetSubstitute($string, ByRef $iCountdown)
    If $iCountdown < 57344 Then Return SetError(1, 0, "") ; Out of options
    Local $sTestChar
    For $i = $iCountdown To 57344 Step -1
        $sTestChar = ChrW($i)
        $iCountdown -= 1
        If Not StringInStr($string, $sTestChar) Then
            Return $sTestChar
        EndIf
    Next
    Return SetError(1, 0, "") ; Out of options
EndFunc ;==> __GetSubstitute

; ==> Example - ORfuscate this script.

; See what it does to an array of numbers:
Local $aArrayOfNumbers[8] = [387, 0xFFFFFFFF, BitOR(1,2,4,8,16,32,64,128), 0.178, 13/167, 0.1e-78, -876, 20]

Global $hFile = FileOpen(@ScriptFullPath)
Global $sScript = FileRead($hFile)
FileClose($hFile)
ConsoleWrite(ORfuscate($sScript) & @CRLF)

#cs Ignores multiline comments
    100, 0xFFFFFFFF, BitOR(23, 57)
#ce

;

Have fun! :)

Bugfix line 123

Edited by czardas
Link to comment
Share on other sites

ORfuscator is aimed at integers only. To use it with strings would require additional pre-processing and I don't think the technique is really suitable (maybe it's worth investigating). Too much pre-processing will make programs sluggish, which I'm trying to avoid. I think there are better methods to use for string obfuscation: for example using regular expressions.

The numbers appearing in code are vitally important for most scripts to function properly, and they are relatively easy to extract and manipulate. I believe it may be possible to exploit this fact in ways that will improve program security. It's early days yet, and there will always be limitations to any technique.

Edited by czardas
Link to comment
Share on other sites

 

ORfuscator is aimed at integers only.

I think there is no point in obfuscating numbers ..

I do not think you can understand tow much from a number .. but string is another story and that what i want to obfuscate.

But i didn't obfuscate string for this reason:

 

with strings would require additional pre-processing and I don't think the technique is really suitable (maybe it's worth investigating). Too much pre-processing will make programs sluggish, which I'm trying to avoid.

 

If you have technique to obfuscate strings without the requirement of additional processing then i would welcome that!

Link to comment
Share on other sites

I think there is no point in obfuscating numbers .

 

Well actually, this depends entirely on the design of your program. The main idea here is to eventually rip some of the numbers out of the code altogether and conceal them somewhere (perhaps in an encrypted key). That will just about break most scripts that use constants. The point being that lots of work will be needed to get the script working again if someone tries to decompile it. This part of the plan still needs consideration.

Can you tell me what the obfuscated code >here is doing? I don't think it's obvious when you first read it, although analysis reveals that it is actually the function Sleep(). Generate lots of code like that and it will be a nightmare to reconstruct the original script. Now suppose someone tries anyway - the level of success will be related to the number of bugs which need fixing. The more subtle the bugs, the longer this process will take. If the debugging takes longer than the release cycle for your program, then it may become a waste of time - although it's only a theory right now and the suitability of such a method will vary.

I don't believe there are any really watertight solutions to program security. You just have to prevent the ship from sinking for the duration of the voyage.

Edited by czardas
Link to comment
Share on other sites

I just wish the official obfuscator was not discontinued. I used to use that to protect a gaming clan's modpack files installer/updater that I wrote to prevent nosey people seeing how the protection worked to easily bypass it.

Edited by Morthawt
Link to comment
Share on other sites

I found some obfuscated scripts quite easy to reconstruct. I only wanted to learn from the process by figuring out how it worked when I attempted to reconstruct the code. I'm trying to find ways around some of the limitations. At least I am learning new things as I go. I may hit a brick wall yet, but I think that's part and parcel of being inquisitive.

Link to comment
Share on other sites

Well actually, this depends entirely on the design of your program. The main idea here is to eventually rip some of the numbers out of the code altogether and conceal them somewhere (perhaps in an encrypted key). That will just about break most scripts that use constants. The point being that lots of work will be needed to get the script working again if someone tries to decompile it. This part of the plan still needs consideration.

Can you tell me what the obfuscated code >here is doing? I don't think it's obvious when you first read it, although analysis reveals that it is actually the function Sleep(). Generate lots of code like that and it will be a nightmare to reconstruct the original script. Now suppose someone tries anyway - the level of success will be related to the number of bugs which need fixing. The more subtle the bugs, the longer this process will take. If the debugging takes longer than the release cycle for your program, then it may become a waste of time - although it's only a theory right now and the suitability of such a method will vary.

I don't believe there are any really watertight solutions to program security. You just have to prevent the ship from sinking for the duration of the voyage.

According to the result of your script, if the new numbers requires additional processing then I think that it is unless to do that..

Unlike like a sentence($sentence , "sentence" , sentence() ), numbers is not something that tell you something..

I think that obfuscating $sentence ,  "sentence" , sentence() + injection confusing code is enough.

but i don't want to obfuscate sentences(this type: "sentence") because I'm afraid that in this way the script will require additional processing..

Your example here:

Will definitely require more processing.

Anyway I am considering to use BinaryToString() for obfuscating sentences.

Is there a better function that takes less processing power?

EDIT:

 

Well actually, this depends entirely on the design of your program. The main idea here is to eventually rip some of the numbers out of the code altogether and conceal them somewhere (perhaps in an encrypted key)

OK,

now i got it..

I would love to use this technique but without encryption/obfuscating numbers.

And about sentences (this type: "sentence") also without encryption because it definitely require additional processing..

about this type of sentences I prefer to use some obfuscation technique which does not require additional processing power.. and I have no idea about such a technique..

Edited by Guest
Link to comment
Share on other sites

gil900 - You raise some valid points. Unfortunately I do not have a general solution for latency incurred. However it should make very little difference in situations where constants are already being Bitwise-OR'ed together - the code might even run faster. I may add an option to exclude a set of functions selected by the developer. This can be done manually at the moment by separating functions (with less critical responce times) into a separate library.

I don't mind a slight delay at startup when security features are running, or when a user initiates a new instance of a process. Many programs display latent behaviour when rollback, or similar, actions are invoked. Automatically recognizing the parts of a script where speed takes priority does not seem feasible, and I don't believe there can ever be a perfect solution that fits all situations.

What I am suggesting here are ideas that can be developed, combined and personalized to suit particular requirements. I don't know of any reliable way to obfuscate code without incurring latency. I'm not saying it's impossible, but it seems that way to me. As regards the example in the other thread - I think you will find the added delay to be less than inherent fluctuations associated with the obfuscated function - Sleep() does not keep perfect time.

Some of the techniques described in this thread can be adapted to work with strings. Unfortunately this seems somewhat more complicated, and I haven't yet come up with a general solution that I'm satisfied with. If I come up with something, you'll be the first to know. :)

Edited by czardas
Link to comment
Share on other sites

OK, I will read the other parts of your post later.

About this:

 

I don't know of any reliable way to obfuscate code without incurring latency. I'm not saying it's impossible, but it seems that way to me.

Yesterday I was thinking about some technique for obfuscating strings..

the idea is to make algorithm that that changes this(example):

$var = "This is sentence"

to this:

$var = "T"
$var &= "h"
$var &= "i"
$var &= "s"
$var &= " "
$var &= "i"
$var &= "s"
$var &= " "
$var &= "s"
$var &= "e"
$var &= "n"
$var &= "t"
$var &= "e"
$var &= "n"
$var &= "c"
$var &= "e"

In large-scale scripts that may be very effective (depending on the person ..).
In the event that there are many sentences in the script, it will add a lot of rows - which is good.

But this idea must upgrade .. you can still read it but it will be less convenient ..

I thought reverse the order of the letters so that it will be someting like this:

 

e
c
n
e
t
n
e
s

s
i

s
i
h
T

And that way it will be more difficult to read..

I think that this method does not require any additional processing power .. but maybe I'm wrong. Even if so, then the addition of processing power that will be required will be approximately 0

 

I don't mind a slight delay at startup when security features are running, or when a user initiates a new instance of a process. Many programs display latent behaviour when rollback, or similar, actions are invoked.

If the idea is to decode the encrypted data(that written in separated place) only when the script/function starts the first time, then i don't mind to incorporate encryption in the obfuscation of this area and scope(I hope you understand what I mean). I even prefer to do so..

Edited by Guest
Link to comment
Share on other sites

The problem with most methods of this kind is that the code can be run and then data extracted. If it is possible to break the code without compromising the compiled script, then you might have more success - I can't guarantee how much. Anyway as I said earlier, ORfuscator is experimental and may also have applications for something else.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...