| ||||||||||||||||
Register Initialising Using Only
Arithmetic Instructions by roy g biv
Register Initialising Using Only Arithmetic Instructions roy g biv / defjam -= defjam =- since 1992 bringing you the viruses of tomorrow today! Former DOS/Win16 virus writer, author of several virus families, including Ginger (see Coderz #1 zine for terrible buggy example, contact me for better sources ;), and Virus Bulletin 9/95 for a description of what they called Rainbow. Co-author of world's first virus using circular partition trick (Orsam, coded with Prototype in 1993). Designer of world's first XMS swapping virus (John Galt, coded by RT Fishel in 1995, only 30 bytes stub, the rest is swapped out). Author of world's first virus using Thread Local Storage for replication (Shrug, see Virus Bulletin 6/02 for a description, but they call it Chiton), world's first virus using Visual Basic 5/6 language extensions for replication (OU812), world's first Native executable virus (Chthon), world's first virus using process co-operation to prevent termination (Gemini, see Virus Bulletin 9/02 for a description), world's first virus using polymorphic SMTP headers (JunkMail, see Virus Bulletin 11/02 for a description), world's first viruses that can convert any data files to infectable objects (Pretext), world's first 32/64-bit parasitic EPO .NET virus (Croissant, see Virus Bulletin 11/04 for a description, but they call it Impanate), world's first virus using self-executing HTML (JunkHTMaiL, see Virus Bulletin 7/03 for a description), world's first virus for Win64 on Intel Itanium (Shrug, see Virus Bulletin 6/04 for a description, but they call it Rugrat), world's first virus for Win64 on AMD AMD64 (Shrug), world's first cross-infecting virus for Intel IA32 and AMD AMD64 (Shrug), world's first viruses that infect Office applications and script files using the same code (Macaroni, see Virus Bulletin 11/05 for a description, but they call it Macar), world's first viruses that can infect both VBS and JScript using the same code (ACDC, see Virus Bulletin 11/05 for a description, but they call it Cada), world's first IDA plugin virus (Hidan), world's first viruses that use the Microsoft Script Encoder to dynamically encrypt the virus body (Screed), world's first virus for StarOffice and OpenOffice (Starbucks), and world's first virus IDC virus (ID10TiC). Author of various retrovirus articles (eg see Vlad #7 for the strings that make your code invisible to TBScan). This is my third virus for Win64. It is the world's first polymorphic virus for Win64 on AMD AMD64. What is it? Probably all polymorphic engines use explicit register initialising. By this, I mean one of these instructions: xor reg, reg sub reg, reg mov reg, value push value / pop reg It means that anyone can see the start of the decryptor because of these instructions. We can try to hide the decryptor by using lots of fake routines and similar tricks, but we can't completely avoid this problem. Or can we? What if we did not use any of those instructions? What if we use only AND and OR to initialise instructions? Then we can have many fake instructions before the real ones, and it is hard to see where the decryptor really starts. We can also add other instructions, like ADD/SUB/XOR. Before the registers are initialised, we can use these instructions to temporarily alter the value, but we must restore the original value before we attempt to update any of the bits. After the registers are initialised, we can use these instructions to select the next value to use. How does it work? We initialise the registers by keeping an array of "unknown" bits for each register, and an array of values for each register. We start by setting all of the "unknown" bits to 1, to say that we don't know any of the values. Whenever we control some of the bits using AND or OR, we clear the corresponding "unknown" bits, and update the same bits in the register values. Once all of the "unknown" bits are cleared, we can begin to use the register, and the whole value is known. This method works for any size of register, so it's even possible on the 64-bit platform. However, only Itanium supports 64-bit immediates. For the AMD64 platform, all 32-bit immediates are treated as signed values, so we must be very careful to use only 31-bit immediates, otherwise the sign-extension can cause unexpected behaviour if we access memory using registers. We use AND and OR because they are the simplest method. For any clear bit in the AND mask, the same "unknown" bits can be cleared, and the register value bits can be cleared. For any set bit in the OR mask, the same "unknown" bits can be set, and the register value bits can be set. Here is an example of that. Let ABCDEFGH be eight unknown bits. Let us perform some operations and see what happens: value unknown ABCDEFGH 11111111 AND 11001100 = AB00EF00 11001100 Four unknown bits left. AB00EF00 11001100 OR 10101010 = 1B001F10 01000100 Two unknown bits left. 1B001F10 01000100 AND 10111011 = 10001010 00000000 No unknown bits left, and our value is fully known! In a more complex case, we can also use ADD, but it can initialise only one bit per round. In that case, an unknown bit that is one position to the left of a known set bit can be cleared by adding the value of the known bit. The problem is that once that is done, every bit to the left of the newly cleared bit becomes unknown, until we reach a known clear bit. At that point, the known clear bit becomes unknown, but any other known bits remain known. Here is an example of that. Let us assume that B and C are 0, and G is 1. value unknown A00DEF1H 10011101 ADD 00000010 = A0CDE10H 10111001 Now we see that B is unchanged, C becomes unknown, and F becomes known. The use of ADD is better when all of the bits to the left of the known bit are unknown, to avoid "losing" bits like the C bit in the example. In some cases, we can also use unexpected instructions like ADC and SBB, even CMP. For example, we know that AND/OR/XOR always clear the carry, so ADC and SBB behave just like ADD and SUB in those cases. Also, if we know the top bit of our register value, even if no other bits are known, then we can use CMP and "guess" the result if the top bit of our register value is different from the top bit of our operation value. Of course, if we know the whole value, then we can use CMP directly to know the result. Entry Point Obscuring Now let us talk about hiding the start of the decryptor. It seems like just using lots of fake instructions would be enough, but it is not so, because when we reach the real decryptor, we will initialise the registers properly, no matter what value they have. There is no way to defend against that, but we can interfere a little bit by altering sensitive registers (like ESP) in the fake instructions. As before, we must restore those registers to their original value if we want to use them, but in the fake instructions before the decryptor, we just "forget" to restore them. This allows us to attack the CPU emulators. A good emulator will see an exception for the bad memory access, and a bad emulator will allow the memory access but sometimes corrupt the decryptor instead. For a register like ESP, where we can never know its true value, there are still some bits that are known. For the 32-bit platform, we know that ESP is always dword-aligned, so we can play with the low two bits. I wanted to completely destroy the ESP value before the decryptor, by using AND and OR with large values, but then I realised that it could be used to know when the decryptor starts, since at that time, such instructions cannot appear. That means that in a sequence like this one: or esp, 12345678 and esp, 87654321 adc esp, 12345678 sub esp, 12345678 or esp, 00000001 <- decryptor starts here and esp, fffffffe the first two instructions can be safely skipped, because they are obviously fake. Instead, I use the allowed values, but I don't restore the register: adc esp, 12345678 or esp, 00000001 <- forget to sub first add esp, 12345678 <- make it worse and esp, ffffffff <- forget to and bit 0 This is harder to detect, and now ESP points randomly in memory. Greets to friendly people (A-Z): Active - Benny - Obleak - Prototype - Ratter - Ronin - RT Fishel - sars - SPTH - The Gingerbread Man - Ultras - uNdErX - Vallez - Vecna - VirusBuster - Whitehead rgb/defjam jul 2006 iam_rgb@hotmail.com |