Mr. Krabs Posted November 14, 2016 Posted November 14, 2016 Difficulty : I guess 5?Language : AutoItPlatform : WindowsOS Version : XP to 10Packer / Protector : AutoGuardIt Description : Simply crack this. I would prefer if you stick to the normal AutoIt cracking method and not treat it like Delphi/C++/C/Compiled languages. Screenshot : ObfuscatedFile.zip
Teddy Rogers Posted November 14, 2016 Posted November 14, 2016 Please attach the crackme and photo... Ted.
SmilingWolf Posted November 25, 2016 Posted November 25, 2016 (edited) While waiting for @kao's paper here is my solution. Full loosely commented Python 2 sources, readme file, even almost all of the garbage files left in place (minus the ones we don't need created by mATE). EDIT: check my last post for all the half assed crap sexy goodness. Achieved: function names decrypted and substituted everywhere. Nothing else. Whatever makes your mill spin, amirite? Spoiler Edited November 27, 2016 by SmilingWolf 3
kao Posted November 26, 2016 Posted November 26, 2016 @SmilingWolf: I don't like publishing half-done tutorials, so you'll have to wait until Tuesday..
SmilingWolf Posted November 26, 2016 Posted November 26, 2016 (edited) On 26/11/2016 at 10:24 AM, kao said: @SmilingWolf: I don't like publishing half-done tutorials, so you'll have to wait until Tuesday.. Nor I like to read half baked stuff (although I do have the habit of releasing half baked scripts that just work(TM) ) Looking forward to it! Meanwhile, purely for the showoff value, here comes a small update Achieved: - function names decrypted and substituted everywhere - switch-based control flow obfuscation removed - dead/unused variables removed using my script, dead/unused functions removed using AU3Stripper Edited November 27, 2016 by SmilingWolf
SmilingWolf Posted November 27, 2016 Posted November 27, 2016 (edited) Aaand I'm pretty much done here. It's RegExes all the way down. AutoGuardIt CrackMe #1.7z Edited November 27, 2016 by SmilingWolf
Mr. Krabs Posted November 29, 2016 Author Posted November 29, 2016 On 11/27/2016 at 4:23 PM, SmilingWolf said: Aaand I'm pretty much done here. It's RegExes all the way down. AutoGuardIt CrackMe #1.7z Thank you for your excellent solution. I will need to work on improving my protection
Solution kao Posted December 2, 2016 Solution Posted December 2, 2016 It took me more time than expected to write it all down. The full writeup is here: http://lifeinhex.com/deobfuscating-autoit-scripts/ Badly-formatted copy-paste follows: ----------------- Every once in a while, someone posts an interesting challenge concerning protected or obfuscated AutoIt scripts. Today I'd like to show some basic approaches to AutoIt deobfuscation. As a target I'll use a very simple protection called AutoGuardIt and the crackme from https://forum.tuts4you.com/topic/39017-autoit-crackme/ Tuts4You thread. If you don't have access to Tuts4You, here is the alternative download link: https://www.mediafire.com/?qs52emp7tkk472g In general, there is nothing hard in decompiling AutoIt scripts. The Autoit script interpreter is designed in such a way that it's really easy to convert P-Code back to the script form. There's also a tidy.exe utility which takes ugly hand-written script and reformats it to make it really pretty. All of this makes writing deobfuscators much easier because you can start with well-formatted AutoIt script and your deobfuscator can consist of simple regexps and string replaces. It will not be very pretty code but it will work. While I was preparing this blog post, SmilingWolf came up with a https://forum.tuts4you.com/topic/39017-autoit-crackme/?do=findComment&comment=187457 full-featured solution written in Python. It's a nice solution but it doesn't explain how or why it works. So, in this article I will explain how the protection works, show the basic techniques and sample source code to defeat each of the protection steps. Making a full-featured deobfuscator is left as an exercise for the reader. Required tools C# compiler. All my examples were tested under Visual Studio 2010 but any recent version should do MyAutToExe. I'm using my personal modification of myAutToExe. You can download it from Bitbucket: https://bitbucket.org/kao/myauttoexe Tool for testing regexps. I'm using http://regexr.com/ Some brains. You can't become a reverser if you can't think for yourself. Decompiling the script There are 2 public tools for extracting compiled AutoIt script: MyAutToExe and Exe2Aut. Exe2Aut uses dynamic approach for obtaining script - it runs the file and gets decrypted and decompressed script from process memory. That's usually the easiest way but you really don't want to run the malware on your computer. MyAutToExe uses static approach - it analyzes file and tries to locate, decrypt and decompress the script on its own. That's more safe approach but it's easier to defeat using different packers, modified script markers and so on. To extract script from this crackme, I used my own MyAutToExe (see "Required tools" section above). Analyzing the obfuscation Once the script is extracted and decompiled, it looks quite strange and unreadable: NTDLLCONFIGEX() Do If 88 - 87 Then Do Assign(Chr(Random(1, 3300 + SRandom(516534452), 1) - 849) & Chr(Random(1, 3867 + SRandom(952848153), 1) - 1209) & Chr(Random(1, 3308 + SRandom(472440744), 1) - 1113) & Chr(Random(1, 2981 + SRandom(204134073), 1) - 1912) & Chr(Random(1, 3216 + SRandom(754454926), 1) - 2967) & Chr(Random(1, 3490 + SRandom(169073304), 1) - 846) & Chr(2432 - Random(1, 2623 + SRandom(66569023), 1)) & Chr(Random(1, 2900 + SRandom(109480308), 1) - 2387) & Chr(Random(1, 2812 + SRandom(30309101), 1) - 854) & Chr(Random(1, 2739 + SRandom(329725379), 1) - 2104) & Chr(Random(1, 2338 + SRandom(751952368), 1) - 1847) & Chr(Random(1, 2255 + SRandom(979815258), 1) - 1586) & Chr(802 - Random(1, 2718 + SRandom(50238634), 1)) & Chr(Random(1, 2441 + SRandom(590667820), 1) - 1556) & Chr(Random(1, 3457 + SRandom(858383127), 1) - 74) & Chr(Random(1, 3062 + SRandom(462948094), 1) - 1332) & Chr(Random(1, 2724 + SRandom(591366195), 1) - 1024) & Chr(Random(1, 3620 + SRandom(28553399), 1) - 1346) & Chr(639 - Random(1, 2693 + SRandom(880254218), 1)) & Chr(Random(1, 2109 + SRandom(681224018), 1) - 1172) & Chr(1917 - Random(1, 2625 + SRandom(230020321), 1)) & Chr(Random(1, 3244 + SRandom(854266612), 1) - 2140) & Chr(Random(1, 2992 + SRandom(341248807), 1) - 1134) & Chr(Random(1, 3571 + SRandom(253330699), 1) - 260) & Chr(Random(1, 3087 + SRandom(999571281), 1) - 729) & Chr(Random(1, 3944 + SRandom(367148632), 1) - 2086) & Chr(Random(1, 2619 + SRandom(143608942), 1) - 1839) & Chr(Random(1, 2436 + SRandom(254454044), 1) - -72) & Chr(Random(1, 2984 + SRandom(138746714), 1) - 2225) & Chr(Random(1, 3920 + SRandom(148637653), 1) - 1816), Execute("UBound")) Until 1 EndIf Until 1 For $NTUSER32POWERSHELLN = 0 To 839.629607988521 Do For $ASSEMBLYAPPLAUNCHLOADERA = 0 To 38.5529099325649 If 147195648 / 96 - 76 = Random(1, 2207 + SRandom(781101274), 1) - -1532371 Then Assign(Chr(Random(1, 2361 + SRandom(696154655), 1) - 479) & Chr(Random(1, 2151 + SRandom(374165062), 1) - 1165) & Chr(Random(1, 3132 + SRandom(871835034), 1) - 2244) & Chr(Random(1, 2703 + SRandom(163537687), 1) - 2429) & Chr(Random(1, 2208 + SRandom(107173462), 1) - 873) & Chr(385 - Random(1, 3300 + SRandom(116179080), 1)) & Chr(Random(1, 2106 + SRandom(526159134), 1) - 537) & Chr(Random(1, 2809 + SRandom(61792761), 1) - 753) & Chr(1730 - Random(1, 3007 + SRandom(54581081), 1)) & Chr(2605 - Random(1, 2526 + SRandom(723252683), 1)) & Chr(Random(1, 2796 + SRandom(68738530), 1) - 2531) & Chr(Random(1, 2532 + SRandom(702895909), 1) - 156) & Chr(1706 - Random(1, 3920 + SRandom(666148063), 1)) & Chr(2785 - Random(1, 2685 + SRandom(367920941), 1)) & Chr(Random(1, 2804 + SRandom(63818035), 1) - 2601) & Chr(Random(1, 2325 + SRandom(643112190), 1) - 2216) & Chr(2320 - Random(1, 2824 + SRandom(115595137), 1)) & Chr(Random(1, 3283 + SRandom(530687995), 1) - 2657) & Chr(Random(1, 3088 + SRandom(175987883), 1) - 230) & Chr(2277 - Random(1, 2607 + SRandom(156297082), 1)) & Chr(Random(1, 2271 + SRandom(271295668), 1) - 1682) & Chr(1054 - Random(1, 2223 + SRandom(981650208), 1)) & Chr(Random(1, 3997 + SRandom(82629437), 1) - 841) & Chr(Random(1, 3586 + SRandom(291610090), 1) - 2993) & Chr(Random(1, 3231 + SRandom(555808060), 1) - 2567) & Chr(3496 - Random(1, 3435 + SRandom(51719875), 1)) & Chr(Random(1, 3235 + SRandom(201120626), 1) - 653) & Chr(Random(1, 2596 + SRandom(229290841), 1) - 1839), Execute("Tan")) EndIf ExitLoop Next Until 1 ExitLoop Next Do For $NTHASHTABLEVOIDUSER32EX = 0 To 399.044101731619 For $NTEXCEPTIONGETINFORMATIONPROCESSERRORSTDOUTEX = 0 To 663.803335762816 Assign(Chr(Random(1, 2266 + SRandom(326424240), 1) - 494) & Chr(Random(1, 3011 + SRandom(93056167), 1) - 1475) & Chr(Random(1, 2899 + SRandom(455451109), 1) - 2318) & Chr(Random(1, 2457 + SRandom(204945), 1) - 928) & Chr(1714 - Random(1, 3307 + SRandom(105031094), 1)) & Chr(Random(1, 3418 + SRandom(925652849), 1) - 2130) & Chr(Random(1, 2311 + SRandom(396157406), 1) - 2200) & Chr(Random(1, 2311 + SRandom(666877116), 1) - -57) & Chr(393 - Random(1, 3921 + SRandom(49978531), 1)) & Chr(Random(1, 2475 + SRandom(494692917), 1) - 2281) & Chr(Random(1, 2353 + SRandom(558419780), 1) - 883) & Chr(Random(1, 3855 + SRandom(783264667), 1) - 620) & Chr(Random(1, 2135 + SRandom(650654129), 1) - 589) & Chr(1790 - Random(1, 3013 + SRandom(515405811), 1)) & Chr(Random(1, 2352 + SRandom(830623589), 1) - 2277) & Chr(Random(1, 3455 + SRandom(41889835), 1) - 2503) & Chr(Random(1, 3905 + SRandom(35527843), 1) - 1812) & Chr(Random(1, 3195 + SRandom(230223745), 1) - 2702) & Chr(Random(1, 3346 + SRandom(278453836), 1) - 2868) & Chr(Random(1, 2332 + SRandom(733194675), 1) - 12) & Chr(Random(1, 3919 + SRandom(779041546), 1) - 1362) & Chr(Random(1, 3754 + SRandom(373790451), 1) - 217) & Chr(Random(1, 3512 + SRandom(726368194), 1) - 2263), Execute("StringSplit")) ExitLoop Next ExitLoop Next Until 1 If 1 Then For $IOBJECTTHREADENVIRONMENTBLOCKW = 0 To 938.744062930811 For $NATIVECONVERSIONA = 0 To 505.457816104172 While 1 Assign(Chr(Random(1, 3747 + SRandom(783912334), 1) - 2281) & Chr(Random(1, 2627 + SRandom(33229718), 1) - 1343) & Chr(Random(1, 3539 + SRandom(186205954), 1) - 3346) & Chr(Random(1, 3739 + SRandom(341316792), 1) - 2933) & Chr(817 - Random(1, 3794 + SRandom(299375926), 1)) & Chr(2218 - Random(1, 3641 + SRandom(337121368), 1)) & Chr(Random(1, 2005 + SRandom(918455413), 1) - 30) & Chr(399 - Random(1, 2099 + SRandom(812497894), 1)) & Chr(1718 - Random(1, 3642 + SRandom(201640616), 1)) & Chr(Random(1, 2022 + SRandom(548220934), 1) - 1893) & Chr(Random(1, 2077 + SRandom(220340072), 1) - 1312) & Chr(2446 - Random(1, 3375 + SRandom(910541033), 1)) & Chr(Random(1, 3834 + SRandom(182516730), 1) - 1863) & Chr(2981 - Random(1, 3611 + SRandom(291650956), 1)) & Chr(Random(1, 2522 + SRandom(786912362), 1) - 1021) & Chr(1237 - Random(1, 2832 + SRandom(105804377), 1)), Execute("StringLen")) ExitLoop WEnd ExitLoop Next ExitLoop Next EndIf ... Let's look at each of the obfuscation techniques and see how it works and how it can be defeated. Integer decomposition AutoGuardIt takes constants and converts them to series of math operations. Example: $VIRTUALIZATIONCIPHERBROWSERA = 71 / 4240 + 4303 + 3057 / 8461 / 4560 + 299 + 7624 * 7110 - 4262 Deobfuscator should be able to take the expression, evaluate it and replace the expression with the correct value. The biggest problem here is the precedence of operations (multiply and divide should be processed before addition and subtraction), so you can't start from the beginning of line and do it one step at a time. This would be wrong: $VIRTUALIZATIONCIPHERBROWSERA = 71 / 4240 + 4303 + 3057 / 8461 / 4560 + 299 + 7624 * 7110 - 4262 $VIRTUALIZATIONCIPHERBROWSERA = 0.0167... + 4303 + 3057 / 8461 / 4560 + 299 + 7624 * 7110 - 4262 $VIRTUALIZATIONCIPHERBROWSERA = 4303.0167... + 3057 / 8461 / 4560 + 299 + 7624 * 7110 - 4262 $VIRTUALIZATIONCIPHERBROWSERA = 7360.0167... / 8461 / 4560 + 299 + 7624 * 7110 - 4262 <---- wrong! ... After some thinking and few Google searches, I found a https://github.com/loresoft/Calculator LoreSoft.MathExpressions library that does all the heavy lifting for me. The following C# code snippet will find all math expressions, extract them, evaluate them and replace expression with the actual value: MathEvaluator eval = new MathEvaluator(); Regex regex2 = new Regex(@"(-)?\d+(( )+[-+*/]( )+([-+])?\d+)+"); for (int i = 0; i < lines.Length; i++) { Match m2 = regex2.Match(lines[i]); while (m2.Success) { double d = eval.Evaluate(m2.Value); lines[i] = regex2.Replace(lines[i], d.ToString(), 1); m2 = m2.NextMatch(); } } Pseudo-random integers This is quite strange protection that relies on a fact that AutoIt's Random function is actually pseudo-random number generator. If you seed it with the same seed, you get the same results. Example: If 194639540 / 20 = Random(1, 3342 + SRandom(753822096), 1) - -9731726 Then $HTTPDELPHIHEADERW = 951.668468197808 EndIf In general, it's a very bad idea because there's no guarantee that random number generator will not change in the next version of AutoIt. But for now it works.. Since I was already using myAutToExe, I decided to use RanRot_MT.dll from the package. [DllImport("ranrot_mt.dll", CallingConvention = CallingConvention.StdCall)] private extern static UInt32 MT_Init(UInt32 seed); [DllImport("ranrot_mt.dll", CallingConvention = CallingConvention.StdCall)] private extern static UInt32 MT_GetI8(); private UInt32 Random(UInt32 from, UInt32 to, UInt32 dummy) { UInt32 result = MT_GetI8() % (to - from + 1) + from; return result; } private UInt32 SRandom(UInt32 seed) { MT_Init(seed); return 1; } // do SRandom/Random Regex regex = new Regex(@"Random\(1, (\d+) \+ SRandom\((\d+)\), 1\)"); for (int i = 0; i < lines.Length; i++) { Match m1 = regex.Match(lines[i]); while (m1.Success) { UInt32 expr1 = UInt32.Parse(m1.Groups[1].Value); UInt32 expr2 = UInt32.Parse(m1.Groups[2].Value); UInt32 rnd = Random(1, expr1 + SRandom(expr2), 1); lines[i] = regex.Replace(lines[i], rnd.ToString(), 1); m1 = m1.NextMatch(); } } a = StringLen("xyz") Small integers can be obfuscated by using function StringLen: $AUTHENTICATIONREFERENCEN = StringLen("UJCzofdlRD") To clean them up, a simple regex can be used: Regex regex3 = new Regex(@"StringLen\(\""([A-Za-z]+)\""\)"); Match m3 = regex3.Match(lines[i]); while (m3.Success) { string expr1 = m3.Groups[1].Value.ToString(); lines[i] = regex3.Replace(lines[i], String.Format("{0}", expr1.Length), 1); m3 = m3.NextMatch(); } End result: $AUTHENTICATIONREFERENCEN = 10 Chr(x) Some strings in the executable are split into bytes, and each byte is then encoded as call to Chr function: Assign(Chr(73) & Chr(68) & Chr(101) & Chr(99) & Chr(114) & Chr(121) & Chr(112) & Chr(116) & Chr(69) & Chr(120) & Chr(101) & Chr(99) & Chr(117) & Chr(116) & Chr(105) & Chr(111) & Chr(110) & Chr(67) & Chr(104) & Chr(97) & Chr(105) & Chr(110) & Chr(67) & Chr(105) & Chr(112) & Chr(104) & Chr(101) & Chr(114) & Chr(69) & Chr(120), Execute("UBound")) Another simple regex will kill all of those: Regex regex3 = new Regex(@"Chr\((\d+)\)"); Match m3 = regex3.Match(lines[i]); while (m3.Success) { UInt32 expr1 = UInt32.Parse(m3.Groups[1].Value); lines[i] = regex3.Replace(lines[i], String.Format("\"{0}\"", (char)expr1), 1); m3 = m3.NextMatch(); } The result will be valid but still hardly readable string: Assign("I" & "D" & "e" & "c" & "r" & "y" & "p" & "t" & "E" & "x" & "e" & "c" & "u" & "t" & "i" & "o" & "n" & "C" & "h" & "a" & "i" & "n" & "C" & "i" & "p" & "h" & "e" & "r" & "E" & "x", Execute("UBound")) And one more simple search-replace will fix that: lines[i] = lines[i].Replace("\" & \"", ""); End result: Assign("IDecryptExecutionChainCipherEx", Execute("UBound")) If 1 Then Once you remove the integer obfuscations, you'll see a lot of useless statements like this: If 1 Then $LOADERHARDWAREIDNULLA = 336.785775121767 EndIf This condition is always true, so we can remove both If and EndIf lines and improve readability. The problem here is that If's can be nested and you can't just remove first EndIf that you encounter. Consider this example: If 1 Then <-- we start here. For $IBINARYDECLAREA = 0 To 826.403731859988 If 1 Then ; do something useful EndIf <-- this is first endif we see, but it's not the right one. ExitLoop Next EndIf <-- this is endif we're looking for Taking all that into account, I came up with this ugly but working* code: if (lines[i].Trim() == "If 1 Then") { int level = 0; int idx = i+1; while (idx < lines.Length) { string s = lines[idx]; if (s.Trim() == "EndIf") { if (level == 0) { // found the proper endif lines[i] = ""; lines[idx] = ""; break; } else level--; } else if (s.Contains("If ")) { level++; } idx++; } } * - see below for some scenarios where this code might fail. If a = a Then Variation of previous protection. In such cases, using regexp is much more efficient than simple string comparison If 1533212 = 1533212 Then ; do something EndIf Do Until 1 It's pretty much the same protection as If 1 Then and it can be defeated the exact same way. Do ; do something Until 1 While 1 / ExitLoop / WEnd Another protection of the same kind, just uses 3 lines of code instead of 2. Same approach, just make sure you match the correct lines and remove all 3 of them. While 1 ; do something ExitLoop WEnd For $random=0 to 123.456 / ExitLoop / Next Another protection, very similar to previous ones. For $NTEXCEPTIONGETINFORMATIONPROCESSERRORSTDOUTEX = 0 To 663.803335762816 Assign("ZwRing3LibraryAssemblyW", Execute("StringSplit")) ExitLoop Next Here one must be very careful not to remove the real for cycles from program, so it's better to use regexps. Apart from that, it's pretty much the same code again. Regex regex4 = new Regex(@"For \$([A-Z0-9])+ = 0 To [0-9]+\.[0-9]+"); Match m4 = regex4.Match(lines[i]); while (m4.Success) { int level = 0; int idx = i + 1; while (idx < lines.Length) { string s = lines[idx]; if (s.Trim() == "Next") { if (level == 0) { // found the proper next if (lines[idx - 1].Trim() == "ExitLoop") { lines[i] = ""; lines[idx - 1] = ""; lines[idx] = ""; } break; } else level--; } else if (s.Contains("For ")) { level++; } idx++; } m4 = m4.NextMatch(); } Assign/Execute This type of protection relies on AutoIt's https://www.autoitscript.com/autoit3/docs/functions/Assign.htm Assign function. First, an alias to a function is defined: Assign("LicenseLoadRing3Ex", Execute("MsgBox")) Later, alias is used to call the function: $LICENSELOADRING3EX(1, BinaryToString(BinaryToString(BinaryToString(BinaryToString(BinaryToString(RING3LOADEREX()))))), BinaryToString(NTCONFIGLICENSELISTW())) Deobfuscation is a simple operation: find all calls to Assign, extract the variable name and the function name, then replace all references to the variable with the function name: MsgBox(1, BinaryToString(BinaryToString(BinaryToString(BinaryToString(BinaryToString(RING3LOADEREX()))))), BinaryToString(NTCONFIGLICENSELISTW())) BinaryToString As you can see in the example above, some strings in the script are replaced with calls to BinaryToString. Here's a another example of the same protection where part of code is replaced with <i>BinaryToString</i> + call to <i>Execute</i>. Local $VIRTUALMACHINESAFECRITICALEX $VIRTUALMACHINESAFECRITICALEX &= BinaryToString("0x4368722852616E646F6D28312C2033363530202B205352616E646F6D28343733333336303430292C203129202D20323130292026204368722852616E646F6D28312C2033323435202B205352616E646F6D28373636323730373731292C203129202D20373132292026204368722852616E646F6D28312C2033333139202B205352616E646F6D28363333383035313430292C203129202D20313832372920262043687228333931202D2052616E646F6D28312C2032353736202B205352616E646F6D28363434353336303331292C203129292026204368722852616E646F6D28312C2032353030202B205352616E646F6D28363538343038303535292C203129202D2032333731292026204368722833343536202D2052616E646F6D28312C2033393633202B205352616E646F6D28343731363230353433292C203129292026204368722852616E646F6D28312C2033363538202B205352616E646F6D28383834353332303635292C203129202D20343632292026204368722831303130202D2052616E646F6D28312C2032313835202B205352616E646F6D28393831353836353633292C203129292026204368722831333338202D2052616E646F6D28312C2033313535202B205352616E646F6D28323738373033303233292C203129292026204368722852616E646F6D28312C2032343835202B205352616E646F6D28353930353836323039292C203129202D20353632292026204368722833323139202D2052616E646F6D28312C2033383033202B205352616E646F6D28393337353439363534292C203129292026204368722831373831202D2052616E646F6D28312C2032303234202B205352616E646F6D28313432323539383133292C203129292026204368722852616E646F6D28312C2033373736202B205352616E646F6D28333737383435393830292C203129202D203239333529202620436872") $VIRTUALMACHINESAFECRITICALEX &= BinaryToString("0x2852616E646F6D28312C2032343436202B205352616E646F6D28393034353431373635292C203129202D2031313535292026204368722852616E646F6D28312C2032383731202B205352616E646F6D28343739303733343431292C203129202D202D3135292026204368722852616E646F6D28312C2033303639202B205352616E646F6D28333639313136333235292C203129202D2031393037292026204368722852616E646F6D28312C2032353838202B205352616E646F6D283134313732323033292C203129202D2032323734292026204368722852616E646F6D28312C2033313539202B205352616E646F6D28393230333036303330292C203129202D2031393739292026204368722831303830202D2052616E646F6D28312C2032363237202B205352616E646F6D28333432353038373031292C203129292026204368722852616E646F6D28312C2032303130202B205352616E646F6D28333331363537373930292C203129202D20313939292026204368722831363735202D2052616E646F6D28312C2033313131202B205352616E646F6D28313238343533323334292C203129292026204368722852616E646F6D28312C2033363530202B205352616E646F6D283139373330383439292C203129202D2031333631292026204368722852616E646F6D28312C2032343731202B205352616E646F6D28383630383637373738292C203129202D20363537292026204368722852616E646F6D28312C2032393230202B205352616E646F6D28323833373631323238292C203129202D2031323437292026204368722852616E646F6D28312C2033383738202B205352616E646F6D28393136313339343131292C203129202D2031393430292026204368722852616E646F6D28312C2032383538202B205352616E646F6D283330323139363430292C203129202D2031393432292026204368722852616E") $VIRTUALMACHINESAFECRITICALEX &= BinaryToString("0x646F6D28312C2033323133202B205352616E646F6D283930303133363136292C203129202D2031303530292026204368722852616E646F6D28312C2033353235202B205352616E646F6D28373139323732363431292C203129202D2032303736292026204368722832313634202D2052616E646F6D28312C2033323232202B205352616E646F6D28353933373831363831292C203129292026204368722852616E646F6D28312C2033383435202B205352616E646F6D28383036323630313036292C203129202D203637342920262043687228373237202D2052616E646F6D28312C2033373434202B205352616E646F6D28363835323338393533292C203129292026204368722852616E646F6D28312C2033353334202B205352616E646F6D28323835383436323133292C203129202D2031373835292026204368722852616E646F6D28312C2033313032202B205352616E646F6D28393530303831313437292C203129202D20323430292026204368722852616E646F6D28312C2032363631202B205352616E646F6D28373532373332373638292C203129202D20363633292026204368722831303733202D2052616E646F6D28312C2032313235202B205352616E646F6D28343838333633333735292C203129292026204368722852616E646F6D28312C2033353532202B205352616E646F6D28333930333938383535292C203129202D2031313137292026204368722852616E646F6D28312C2032363933202B205352616E646F6D28383535333633353035292C203129202D203138373429") $IENUMERATORENGINENATIVECALLEX = Execute($VIRTUALMACHINESAFECRITICALEX) Merging all 3 lines into one and converting hex strings to bytes gives us the following code: $IENUMERATORENGINENATIVECALLEX = Chr(Random(1, 3650 + SRandom(473336040), 1) - 210) & Chr(Random(1, 3245 + SRandom(766270771), 1) - 712) & Chr(Random(1, 3319 + SRandom(633805140), 1) - 1827) & Chr(391 - Random(1, 2576 + SRandom(644536031), 1)) & Chr(Random(1, 2500 + SRandom(658408055), 1) - 2371) & Chr(3456 - Random(1, 3963 + SRandom(471620543), 1)) & Chr(Random(1, 3658 + SRandom(884532065), 1) - 462) & Chr(1010 - Random(1, 2185 + SRandom(981586563), 1)) & Chr(1338 - Random(1, 3155 + SRandom(278703023), 1)) & Chr(Random(1, 2485 + SRandom(590586209), 1) - 562) & Chr(3219 - Random(1, 3803 + SRandom(937549654), 1)) & Chr(1781 - Random(1, 2024 + SRandom(142259813), 1)) & Chr(Random(1, 3776 + SRandom(377845980), 1) - 2935) & Chr(Random(1, 2446 + SRandom(904541765), 1) - 1155) & Chr(Random(1, 2871 + SRandom(479073441), 1) - -15) & Chr(Random(1, 3069 + SRandom(369116325), 1) - 1907) & Chr(Random(1, 2588 + SRandom(14172203), 1) - 2274) & Chr(Random(1, 3159 + SRandom(920306030), 1) - 1979) & Chr(1080 - Random(1, 2627 + SRandom(342508701), 1)) & Chr(Random(1, 2010 + SRandom(331657790), 1) - 199) & Chr(1675 - Random(1, 3111 + SRandom(128453234), 1)) & Chr(Random(1, 3650 + SRandom(19730849), 1) - 1361) & Chr(Random(1, 2471 + SRandom(860867778), 1) - 657) & Chr(Random(1, 2920 + SRandom(283761228), 1) - 1247) & Chr(Random(1, 3878 + SRandom(916139411), 1) - 1940) & Chr(Random(1, 2858 + SRandom(30219640), 1) - 1942) & Chr(Random(1, 3213 + SRandom(90013616), 1) - 1050) & Chr(Random(1, 3525 + SRandom(719272641), 1) - 2076) & Chr(2164 - Random(1, 3222 + SRandom(593781681), 1)) & Chr(Random(1, 3845 + SRandom(806260106), 1) - 674) & Chr(727 - Random(1, 3744 + SRandom(685238953), 1)) & Chr(Random(1, 3534 + SRandom(285846213), 1) - 1785) & Chr(Random(1, 3102 + SRandom(950081147), 1) - 240) & Chr(Random(1, 2661 + SRandom(752732768), 1) - 663) & Chr(1073 - Random(1, 2125 + SRandom(488363375), 1)) & Chr(Random(1, 3552 + SRandom(390398855), 1) - 1117) & Chr(Random(1, 2693 + SRandom(855363505), 1) - 1874) which using the methods described earlier can be deobfuscated as: $IENUMERATORENGINENATIVECALLEX = "ZwConventionReflectionAuthenticationN" Functions returning constants Some strings are not only encoded using BinaryToString but also moved to a separate function. Local $ASSIGNASSEMBLYEX = DllStructCreate(BinaryToString(DECRYPTSOCKETW())) ... Func DECRYPTSOCKETW($IENUMERATORDECRYPTIONA = 9887.61287710024, $SESSIONUSER32APPLAUNCHEX = 4750.21296840813) Local $WINAPIUSERBASEN $DRIVEREXECUTIONCHAINDLLN = "0x75696E743B64776F7264" Return $DRIVEREXECUTIONCHAINDLLN EndFunc Deobfuscated code will look like this: Local $ASSIGNASSEMBLYEX = DllStructCreate(BinaryToString("0x75696E743B64776F7264")) It's quite tricky to find the correct return value for each function and replace function calls with correct values. In addition to that, regexes aren't really suitable for such tasks. The code I wrote is really ugly, so I'm not going to show it. Go, figure it out yourself! Switch control-flow obfuscation This is actually the hardest one of them all. The example code looks like this: $1375150359 = 727533448 ... Switch $1375150359 Case 727533448 OBFUSCATEDFREEMEMORYTHREADN($NTVARIABLENATIVECALLCONNNECTN) $1375150359 = 834946917 Case 834946917 Local $ASSIGNCIPHERALLOCATEEX = $BINARYPOINTERHASHTABLEN $1375150359 = 1646727737 Case 1561719614 Local $LIBRARYINTEX = $OBJECTDLLUSERBASEEX(NTASSEMBLYREFLECTIONBROWSERN(), $VOIDKERNELA(ICRYPTOGRAPHYENGINEN()), $VOIDKERNELA(IBYTEOBFUSCATIONMEMORYN()), $VOIDKERNELA(PROXYENGINEMEMBERN()), 0, $VOIDKERNELA(IESCALATIONVIRTUALIZATIONN()), 0, $VOIDKERNELA(IESCALATIONVIRTUALIZATIONN()), 0, $VOIDKERNELA(LICENSEDECRYPTIONEXECUTEN()), $ASSIGNCIPHERALLOCATEEX, $VOIDKERNELA(LICENSEDECRYPTIONEXECUTEN()), $LICENSELOADKERNEL32SOCKETEX) ExitLoop Case 1646727737 $DECLAREWRAPPERPROXYENGINEA = $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 3169 + SRandom(653589833), 1) - 2528) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 2474 + SRandom(380961127), 1) - 339) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 3978 + SRandom(827933013), 1) - 3839) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 2462 + SRandom(36794804), 1) - 1794) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 3989 + SRandom(628400507), 1) - -107) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX(177 - $ZWLOADERHASHW(1, 2867 + SRandom(441699849), 1)) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 2037 + SRandom(987330901), 1) - -36) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 3730 + SRandom(95099852), 1) - 3081) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 2966 + SRandom(515060101), 1) - 432) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 2583 + SRandom(701470155), 1) - 2082) $1375150359 = 1561719614 EndSwitch You must find the initial value of the variable. Then you must find the correct start/end of the Switch, all the switch cases and all other assignments to the control variable. Then you'll be able to reorder all the code. It's a moderately hard problem for which I don't have a pretty solution. Here's my code which seems to work: Dictionary<string, string> assignedValues = new Dictionary<string, string>(); Regex assignVal = new Regex(@"(\$[0-9]+) = ([0-9]+)"); // $1375150359 = 727533448 Regex switchVal = new Regex(@"Switch (\$[0-9]+)"); // Switch $1375150359 Regex caseVal = new Regex(@"Case ([0-9]+)"); // Case 1561719614 for (int i = 0; i < lines.Length; i++) { Match m1 = assignVal.Match(lines[i]); if (m1.Success) { string s1 = m1.Groups[1].ToString(); if (!assignedValues.ContainsKey(s1)) { assignedValues.Add(s1, m1.Groups[2].ToString()); } else { assignedValues[s1] = m1.Groups[2].ToString(); } } else { Match m2 = switchVal.Match(lines[i]); if (m2.Success) { // found a switch. now find endswitch. create a list of cases. string switchVariableName = m2.Groups[1].ToString(); int i2 = i + 1; Dictionary<string, int> cases = new Dictionary<string, int>(); while (lines[i2].Trim() != "EndSwitch") { Match m3 = caseVal.Match(lines[i2]); if (m3.Success) { cases.Add(m3.Groups[1].ToString(), i2); } i2++; } cases.Add("EndSwitch", i2); // reorder lines. List<string> orderedLines = new List<string>(); Regex switchVarAssignment = new Regex(String.Format(@"\{0} = ([0-9]+)",switchVariableName)); //$1375150359 = 834946917 string nextValue = assignedValues[switchVariableName]; // starting with the initial value while (nextValue != null) { int idx = cases[nextValue] + 1; nextValue = null; while (!cases.ContainsValue(idx)) { Match m4 = switchVarAssignment.Match(lines[idx]); if (m4.Success) { nextValue = m4.Groups[1].ToString(); } else { orderedLines.Add(lines[idx]); } idx++; } } // replace all the original lines with spaces.. for (int k = i; k <= i2; k++) { lines[k] = ""; } // overwrite first X lines with ordered ones.. for (int k = 0; k < orderedLines.Count; k++) { lines[i + k] = orderedLines[k]; } } } } After cleanup, the deobfuscated code looks like this: OBFUSCATEDFREEMEMORYTHREADN($NTVARIABLENATIVECALLCONNNECTN) Local $ASSIGNCIPHERALLOCATEEX = $BINARYPOINTERHASHTABLEN $DECLAREWRAPPERPROXYENGINEA = $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 3169 + SRandom(653589833), 1) - 2528) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 2474 + SRandom(380961127), 1) - 339) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 3978 + SRandom(827933013), 1) - 3839) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 2462 + SRandom(36794804), 1) - 1794) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 3989 + SRandom(628400507), 1) - -107) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX(177 - $ZWLOADERHASHW(1, 2867 + SRandom(441699849), 1)) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 2037 + SRandom(987330901), 1) - -36) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 3730 + SRandom(95099852), 1) - 3081) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 2966 + SRandom(515060101), 1) - 432) & $THREADENVIRONMENTBLOCKINITIALIZATIONLIBRARYEX($ZWLOADERHASHW(1, 2583 + SRandom(701470155), 1) - 2082) Local $LIBRARYINTEX = $OBJECTDLLUSERBASEEX(NTASSEMBLYREFLECTIONBROWSERN(), $VOIDKERNELA(ICRYPTOGRAPHYENGINEN()), $VOIDKERNELA(IBYTEOBFUSCATIONMEMORYN()), $VOIDKERNELA(PROXYENGINEMEMBERN()), 0, $VOIDKERNELA(IESCALATIONVIRTUALIZATIONN()), 0, $VOIDKERNELA(IESCALATIONVIRTUALIZATIONN()), 0, $VOIDKERNELA(LICENSEDECRYPTIONEXECUTEN()), $ASSIGNCIPHERALLOCATEEX, $VOIDKERNELA(LICENSEDECRYPTIONEXECUTEN()), $LICENSELOADKERNEL32SOCKETEX) ExitLoop Unused variable assignments There are random variable assignments sprinkled all over the code. $ZWPROGRAMCONVENTIONW = 37455182.0495048 $IVOIDARCHCONTROLFLOWA = "NtSSLReflectionNullEx" They are only assigned once and never used. To clean them up, one can locate the assignments using regex, count how many times this variable appears in the code and remove it, if it's only assigned once. Something like this: Regex assignFloat = new Regex(@"^\s*(\$[A-Z0-9]+) = \-?[0-9]+\.[0-9]+"); for (int i = 0; i < lines.Length; i++) { Match m1 = assignFloat.Match(lines[i]); if (m1.Success) { string variableName = m1.Groups[1].ToString(); int count = 0; for (int i2 = 0; i2 < lines.Length; i2++) { if (lines[i2].Contains(variableName)) count++; } if (count == 1) { lines[i] = ""; } } } You can remove string and integer assignments using very similar regex. Lather, rinse, repeat If you run each of the mentioned deobfuscations only once, you'll end up with half-deobfuscated code. Also, there isn't one specific order in which the deobfuscations should be applied. Of course, you could just run the entire loop 100 times, but it's just ugly. Therefore, I prefer to run the code in the loop like this: bool modified = true; while (modified) { modified = DeobIntegerDecomposition(); modified |= DeobPseudoRandom(); modified |= DeobChr(); ... } It will run all the deobfuscations until there's nothing left to clean up. Then run tidy.exe on the output and you'll get a nicely readable script.. Possible problems and gotchas Deobfuscators based on string matching are very easy to implement. However, one must be very careful to write correct regular expressions and string comparisons. My example code works very well on this specific crackme. However, it will mess up with code like this: If 1 Then $A = "If at first you don't succeed, try, try again." <-- Cleaner will find string "If " and mess up counts. EndIf <-- So this EndIf will not be removed. ... <-- Cleaner will find another "EndIf" in code below, or just crash You can work around such issues by using more specific regexes with anchors. During my research, I used http://regexr.com/ a lot, and I find it really helpful. Give it a try! Conclusion In this article I showed basic approaches that can be used to deobfuscate (not only) AutoIt scripts. They are extremely simple and work well for one-time tasks. For more complex tasks, deobfuscators based on https://en.wikipedia.org/wiki/Abstract_syntax_tree abstract syntax trees or disassembled P-Code are much more efficient but more time-consuming to create. Have fun! -------- Attached is deobfuscated script. ObfuscatedFile-fix.zip 9
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now