Cracking Software Protections

advertisement
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.
Download