Athcon 0x7DB . Exploiting Anti-Reversing Techniques -A Case Study By Economou kyriakos- Attacking Armadillo’s Loader under Xenocode Application Virtualization. What is Armadillo… Commercial Software Protection It is actually an executable/dll packer. Offers very good protection against Reverse Engineering. It is very popular among commercial software developers. Armadillo’s Anti-Reversing Tricks PE Header Elimination Code Splicing Imports Redirection IAT Elimination Debug Blocker CopyMemII Nanomites Debugger Detection through IsDebuggerPresent() Crashes Olly Debugger through OutputDebugString() with strings of “%s%s%s%s” format. CRC checks on runtime. What is Xenocode… A set of application virtualization and portable application creation technologies developed by Code Systems Corporation. On June 5, 2010 the name Xenocode was changed to Spoon. Xenocode packs the whole application including its modules and dependencies into a single executable file. It actually emulates the OS features necessary for the application to run such as DLL files and even entire runtimes and components like .NET Framework 2.0, 3.0, 3.5, and 4.0, Java, AIR, Silverlight, and Flash. The virtualized application runs independently from the other software that is installed on the host PC. Has Armadillo been reverse engineered? YES!!! Does Xenocode provide any anti-piracy protection? NO!!! -at least not on purpose- Then, why bother?!? This is the first time that Armadillo packer gets involved into application virtualization. There is a lot to learn regarding “choosing the appropriate software protection” because… …we will demonstrate a case in which we can exploit an anti-reversing technique, thus use it against the protection itself. Our case… The virtualized application has been written for the .net framework. The Armadillo protection had been applied to one of its DLL libraries before the virtualization. When we run the application, Xenocode will load in memory all the necessary modules including the Armadillo packed DLL, which is used for the license management (trial days remaining, license verification etc..) The tricky part… All the necessary modules are loaded on run-time -no HD I/O-, hence no physical access to these files on disk -No patching!-. We could attempt to dump from memory and modify the Armadillo packed DLL or any other module, but that would be useless, because Xenocode will always load in memory the original one. The ImageBase of all modules including the Armadillo packed DLL is always different in memory when loaded. The Armadillo packed DLL will allocate some extra memory on run-time and will write there some extra code where the license system performs its checks -also known as “ArmAcess.dll”- from the earlier versions of Armadillo. …which means that the VA of that specific code block regarding the license check will also be different every time. So know what?!? We need to code a loader that will always locate in memory that specific code block and modify it on runtime…. ….or we could try to write our own inline functions inside the executable itself that will do the job on run-time. Our way in… The virtual DLL written somewhere in memory by the Armadillo packed DLL is going to use the OutputDebugString() win API in order to crash Olly Debugger. This is a specific anti-debugger attack, but it is used anyways. We can use this fact as our way into the Armadillo’s virtual DLL (ArmAccess.dll) …and from the stack we shall learn Enter the ArmAccess.dll Licence check references Follow back in Disassembler The magic byte issue It is always zero by default. It must “always” be !=0 The VA where this byte is located it is always different due to the dynamic ImageBase of the virtual DLL. We need to know every time what is the VA of the magic byte. Putting things together We can always know a VA in the address space of the virtual DLL through the stack when the OutputDebugString API is called. We also now know a VA of the magic byte. The virtual DLL’s structure is always the same, hence the difference in bytes between those 2 locations is constant. We just need the RVA of the magic byte to a VA where a call to OutputDebugString API is made from the virtual DLL. Calculating magic byte’s RVA MagicByteRVA = MagicByteVA – RET from CALL to OutputDebugStringVA Where: MagicByteVA = The VA of the magic byte in this running instance of the process. RET from CALL to OutputDebugStringVA = The RET VA after a CALL to OutputDebugStringA from the virtual DLL. In this case: MagicByteRVA = 0x6323E1E – 0x62677CD = 0xBC651 Things to keep in mind The RVA calculated will be always constant if… You always refer to the VA of the same CALL to OutputDebugString from the virtual DLL, because more CALLs may be made from different location inside the virtual DLL. You work on the same version of the specific application. Issues to take in consideration The license check routine may be executed more than once inside the virtual DLL which means that: The state of the magic byte may be changed back again to zero. You need to know after how many times the OutputDebugString is called, it is safe to modify the magic byte… …which means that in this software protection mechanism you may need to calculate the RVA of the magic byte by counting how many times the OutputDebugString has been called and taking in consideration the “safe” VA where a call to OutputDebugString from the virtual DLL is made in different versions of the same application. One step further… We cannot write an inline function that directly modifies the magic byte in the Armadillo’s license system, because this code it is going to be written somewhere in memory on run-time by the Armadillo packed DLL. We need to prepare the inline function that is going to be executed on runtime while we still control the executable from where the Xenocode VM is going to be launched. We are going to exploit the OutputDebugStringA anti-reversing trick by modifying the API’s code inside kernel32.dll or kernelbase.dll depending on Windows version. This modification must occur on run-time by another inline function each time we launch the application. Chained inlining – 1st Inline Function • We first need to add some extra strings inside the code cave. • Notice that we use unicode for the system DLL names. This is because the application is using the unicode version of LoadLibrary API which we also need. Therefore, we will use in our favour application’s IAT too… • • • • • • • • • • 00404C54 00404C64 00404C72 00404C73 00404C83 00404C8D 00404C8E 00404C9E 00404CA1 00404CA2 . 6B00 6500 720>UNICODE "kernelba“ . 7300 6500 2E0>UNICODE "se.dll",0 00 DB 00 . 6B00 6500 720>UNICODE "kernel32“ . 2E00 6400 6C0>UNICODE ".dll",0 00 DB 00 . 4F 75 74 70 7>ASCII "OutputDebugStrin" . 67 41 00 ASCII "gA",0 00 DB 00 . 56 69 72 74 7>ASCII "VirtualProtect",0 Chained inlining – 1st Inline Function • • • • • • • • • • • • • • • • • • • • 00404CB5 > 55 PUSH EBP 00404CB6 . 8BEC MOV EBP, ESP 00404CB8 . 83E4 F8 AND ESP, FFFFFFF8 00404CBB . 60 PUSHAD 00404CBC . 9C PUSHFD 00404CBD . 68 544C4000 PUSH MindWork.00404C54 ; /FileName = "kernelbase.dll" 00404CC2 . FF15 50608B00 CALL DWORD PTR DS:[<&KERNEL32.LoadLib>; \LoadLibraryW 00404CC8 . 85C0 TEST EAX, EAX 00404CCA . 75 0B JNZ SHORT MindWork.00404CD7 00404CCC . 68 734C4000 PUSH MindWork.00404C73 ; /FileName = "kernel32.dll" 00404CD1 . FF15 50608B00 CALL DWORD PTR DS:[<&KERNEL32.LoadLib>; \LoadLibraryW 00404CD7 > 8BF0 MOV ESI, EAX 00404CD9 . 68 8E4C4000 PUSH MindWork.00404C8E ; /ProcNameOrOrdinal = "OutputDebugStringA“ 00404CDE . 50 PUSH EAX ; |hModule 00404CDF . FF15 4C608B00 CALL DWORD PTR DS:[<&KERNEL32.GetProc>; \GetProcAddress 00404CE5 . 8BF8 MOV EDI, EAX 00404CE7 . 68 A24C4000 PUSH MindWork.00404CA2 ; /ProcNameOrOrdinal = "VirtualProtect" 00404CEC . 56 PUSH ESI ; |hModule 00404CED . FF15 4C608B00 CALL DWORD PTR DS:[<&KERNEL32.GetProc>; \GetProcAddress 00404CF3 . 8BF0 MOV ESI, EAX Chained inlining – 1st Inline Function • • • • • • • • • • • • • • • • • • • • • • 00404CF5 00404CFA 00404CFC 00404CFE 00404CFF 00404D01 00404D03 00404D06 00404D09 00404D0F 00404D12 00404D18 00404D1C 00404D1F 00404D23 00404D2A 00404D31 00404D38 00404D3F 00404D46 00404D47 00404D48 . 68 00504000 PUSH MindWork.00405000 . 6A 40 PUSH 40 . 6A 24 PUSH 24 . 57 PUSH EDI . FFD6 CALL ESI . 8BF7 MOV ESI, EDI . 83C6 24 ADD ESI, 24 . C606 00 MOV BYTE PTR DS:[ESI], 0 . C707 609C8005 MOV DWORD PTR DS:[EDI], 5809C60 . 8977 04 MOV DWORD PTR DS:[EDI+4], ESI . 66:C747 08 01>MOV WORD PTR DS:[EDI+8], 8001 . C647 0A 3D MOV BYTE PTR DS:[EDI+A], 3D . 8977 0B MOV DWORD PTR DS:[EDI+B], ESI . C647 0F 03 MOV BYTE PTR DS:[EDI+F], 3 . C747 10 74039>MOV DWORD PTR DS:[EDI+10], 619D0374 . C747 14 C38B4>MOV DWORD PTR DS:[EDI+14], 24448BC3 . C747 18 2405B>MOV DWORD PTR DS:[EDI+18], 6BBB0524 . C747 1C 0B00C>MOV DWORD PTR DS:[EDI+1C], 0C6000B . C747 20 019D6>MOV DWORD PTR DS:[EDI+20], C3619D01 . 9D POPFD . 61 POPAD .^ E9 58F4FFFF JMP MindWork.004041A5 Chained inlining – 2nd Inline Function When the 1st inline function ends, the OutputDebugString API will have been changed into the following… • • • • • • • • • • • • • • • 759FD4F5 > 60 PUSHAD 759FD4F6 9C PUSHFD 759FD4F7 8005 19D59F75 0>ADD BYTE PTR DS:[759FD519], 1 759FD4FE 803D 19D59F75 0>CMP BYTE PTR DS:[759FD519], 3 759FD505 74 03 JE SHORT KERNELBA.759FD50A 759FD507 9D POPFD 759FD508 61 POPAD 759FD509 C3 RET 759FD50A 8B4424 24 MOV EAX, DWORD PTR SS:[ESP+24] 759FD50E 05 BB6B0B00 ADD EAX, 0B6BBB 759FD513 C600 01 MOV BYTE PTR DS:[EAX], 1 759FD516 9D POPFD 759FD517 61 POPAD 759FD518 C3 RET 759FD519 00 The 3rd time we arrive here, this function is going to retrieve the return address to the Armaccess.dll, calculate the RVA between that address and magic byte’s address and modify it to 1. A lesson to learn A software protection that it is good for one case may not suit your needs. In fact, the software I worked on for this presentation was considered “uncrackable”! Even worse, it may be proved your worst enemy, and remember you have paid for it! An anti-reversing trick was turned into a path inside the protection itself, think about it! Next time, put some anti-reversing tricks inside your code too, and don’t rely only to the commercial software protection… …and dude, use a little bit of imagination. I did! :o) Thank you :0) There are 10 types of people in the world: Those who understand binary and those who...have a life.