神刀安全网

X86 Shellcode Obfuscation – Part 3: Execution Flow Deception

Hello and welcome back to the shellcode obfuscation series! If you’ve missed the previous episodes, take your time and catch up here:

X86 Shellcode Obfuscation – Part 1

X86 Shellcode Obfuscation – Part 2

Last time, I’ve added obfuscation support for most common x86 instructions, which allowed to process the obfuscation output several times in order to get even better results. The obfuscated code output now, while being pretty well obfuscated, still is pretty easy to navigate as the execution flow is not changed. I will fix it this episode as I explain methods of implementing full blown execution flow obfuscation by injecting dozens of jumps to make the code output unrecognizable.

Execution Flow Deception

Every assembly code consists of calls and jumps separated by instructions that reside in the middle. These are commonly called branch instructions as they allow the code to branch out into multiple directions.

Let’s take a look at the following code:

00390000   B8 02000000      MOV EAX,2   00390005   B9 06000000      MOV ECX,6   0039000A   03C1             ADD EAX,ECX   0039000C   85C0             TEST EAX,EAX   0039000E   74 05            JE SHORT 00390015   00390010   83F8 08          CMP EAX,8   00390013  ^75 EB            JNZ SHORT 00390000   00390015   C3               RETN   

This simple code puts some values in EAX and ECX registers. After that it adds ECX to EAX and jumps to 00390015 if EAX == 0 (which is false). Later it compares if the value of EAX == 8 and if not, jumps back to the beginning. If EAX == 8 the jump is not taken and RETN is executed.

That sample code will be used for demonstration purposes. You can see the code is pretty straight forward and easy to navigate. Is there any way we can disturb its execution flow to make it both harder to read and harder to step through? There sure is!

The purpose of execution flow obfuscation will be to add as many unnecessary jumps as possible, while retaining the original code functionality. The obfuscation tool should go over every instruction one by one and decide at each step wether the jump should be placed at that spot. If it decides the jump is to be placed, it will pick a random place in the shellcode to jump to. The "jump-to" place will need to handle inserted code, but as there will already be other code in this place, we need to split the code with another jump, that will jump over our inserted code.It may be a bit hard to explain in words, but hopefully it gets clearer with the following example.

Lets say we are inserting a jump at 00390010 and we randomly picked instruction at address 0039000A to insert into. This is how our sample code will look like after the jump is inserted:

00390000   B8 02000000      MOV EAX,2   00390005   B9 06000000      MOV ECX,6   0039000A   EB 06            JMP SHORT 00390012 <-- inserted code with a jump split, jumping over the inserted code   0039000C   83F8 08          CMP EAX,8   0039000F  ^75 EF            JNZ SHORT 00390000   00390011   C3               RETN               <-- inserted code ends here   00390012   03C1             ADD EAX,ECX   00390014   85C0             TEST EAX,EAX   00390016  ^74 F9            JE SHORT 00390011   00390018  ^EB F2            JMP SHORT 0039000C <-- inserted jump   

You can see that the code was split at 0039000A with a jump that jumps over the inserted code. The execution order is preserved, but the flow is now different. If you follow the instructions one by one, you can see that the instructions are executed in perfect order as they were in the original sample.

This is basically the whole idea. Not much to add here, but what if we can up the game with…

Anti-disassembly Tricks

I will focus here on a very popular method that involves inserting a wild byte in-between instructions, which is never executed, but effectively makes byte-by-byte disassemblers trip and fail miserably. Smart disassemblers that follow the execution flow of instructions, won’t be fooled by this (IDA is handling this very well), but other debuggers/disassemblers that focus on disassembling instructions one by one from top to bottom will have problems (like OllyDbg).

Let’s imagine we have the following code:

00390000   B9 01000000      MOV ECX,1   00390005   03C1             ADD EAX,ECX   

Now we insert a jump at 00390000 that will point to the next instruction:

00390000   EB 00            JMP SHORT 00390002   00390002   B9 01000000      MOV ECX,1   00390007   03C1             ADD EAX,ECX   

Simple right? Now that the jump is in place and it is set to jump to the next instruction, what will happen if we insert 0xE8 byte in the middle between JMP SHORT and MOV ECX,1 ?

00390000   EB 00            JMP SHORT 00390003   00390002   E8 B9010000      CALL 003901C0   00390007   0003             ADD BYTE PTR DS:[EBX],AL   00390009   C100 00          ROL DWORD PTR DS:[EAX],0   

As you can see, the disassembler that tried to disassemble instructions one by one, came across our 0xE8 byte and interpreted it as a next instruction opcode, which is a CALL followed by an immediate relative address. The disassembler doesn’t know that the inserted 0xE8 wild byte is never executed and instead tries its best to make sense of the machine code it sees. This one-byte shift also affects disassembly of other instructions that follow.

The obfuscation tool inserts a lot of jumps in order to change the execution flow and it would be a waste if we didn’t put some wild bytes, after the jumps, here and there to make the disassembled code practically unreadable in debuggers/disassemblers that do not analyze the execution flow.

Here is the full list of wild bytes that I decided to use with their corresponding instruction opcodes:

68 - PUSH IMM32   81 - ADD/OR/ADC/SBB/AND/SUB/XOR IMM32   83 - ADD/OR/ADC/SBB/AND/SUB/XOR IMM8   D8 - FADD/FMUL/FCOM/FCOMP/FSUB/FSUBR/FDIV/FDIVR M32REAL   D9 - FLD/FXCH/FST/FNOP/...   DA - FIADD/FCMOVB/FIMUL/FCMOVE/FICOM/...   DC - FADD/FMUL/FCOM/FCOMP/FSUB/FSUBR/FDIV/FDIVR M64REAL   DE - FIADD/FCMOVB/FIMUL/FCMOVE/FICOM/...   E8 - CALL   E9 - JMP   EA - JMP FAR   F7 - TEST/NOT/NEG/MUL/IMUL/DIV/IDIV   

Conclusion

With all of the obfuscation features in place, let’s see what will happen if we run the sample code through the 1-pass obfuscation process with highest level of control flow mixing. For reference this is the untouched sample code in its original form:

0    00000000: b802000000                       MOV EAX, 0x2   1    00000005: b906000000                       MOV ECX, 0x6   2    0000000a: 01c8                             ADD EAX, ECX   3    0000000c: 85c0                             TEST EAX, EAX   4    0000000e: 7405                             JZ 0x15   5    00000010: 83f808                           CMP EAX, 0x8   6    00000013: 75eb                             JNZ 0x0   7    00000015: c3                               RET   

And here is the same code after obfuscation:

0    00000000: b837272a61                       MOV EAX, 0x612a2737   1    00000005: eb3a                             JMP 0x41   2    00000007: eb2f                             JMP 0x38   3    00000009: 81c06de78ae1                     ADD EAX, 0xe18ae76d   4    0000000f: eb16                             JMP 0x27   5    00000011: 81e87daf9577                     SUB EAX, 0x7795af7d   6    00000017: e994000000                       JMP 0xb0   7    0000001c: 83   7    0000001d: eb08                             JMP 0x27   8    0000001f: bb0089c4e5                       MOV EBX, 0xe5c48900   9    00000024: eb53                             JMP 0x79   10   00000026: 68   10   00000027: ebe8                             JMP 0x11   11   00000029: f7   11   0000002a: eb0c                             JMP 0x38   12   0000002c: 81e9699dec08                     SUB ECX, 0x8ec9d69   13   00000032: e9ab000000                       JMP 0xe2   14   00000037: 83   14   00000038: 81c038e2d5f6                     ADD EAX, 0xf6d5e238   15   0000003e: ebc9                             JMP 0x9   16   00000040: d8   16   00000041: eb56                             JMP 0x99   17   00000043: eb4b                             JMP 0x90   18   00000045: eb05                             JMP 0x4c   19   00000047: 75b7                             JNZ 0x0   20   00000049: eb2d                             JMP 0x78   21   0000004b: e9   21   0000004c: b995f3fde5                       MOV ECX, 0xe5fdf395   22   00000051: eb1a                             JMP 0x6d   23   00000053: eb13                             JMP 0x68   24   00000055: 81ebef03dbd0                     SUB EBX, 0xd0db03ef   25   0000005b: eb08                             JMP 0x65   26   0000005d: 39d8                             CMP EAX, EBX   27   0000005f: e98f000000                       JMP 0xf3   28   00000064: 81   28   00000065: eb51                             JMP 0xb8   29   00000067: ea   29   00000068: 01c8                             ADD EAX, ECX   30   0000006a: eb06                             JMP 0x72   31   0000006c: d8   31   0000006d: ebbd                             JMP 0x2c   32   0000006f: 81   32   00000070: eb13                             JMP 0x85   33   00000072: 85c0                             TEST EAX, EAX   34   00000074: eb0c                             JMP 0x82   35   00000076: eb01                             JMP 0x79   36   00000078: c3                               RET   37   00000079: 81ebeb56863b                     SUB EBX, 0x3b8656eb   38   0000007f: eb52                             JMP 0xd3   39   00000081: d8   39   00000082: eb27                             JMP 0xab   40   00000084: f7   40   00000085: eb09                             JMP 0x90   41   00000087: 81e93cdbac3f                     SUB ECX, 0x3facdb3c   42   0000008d: ebd9                             JMP 0x68   43   0000008f: ea   43   00000090: 81c0c757b8d5                     ADD EAX, 0xd5b857c7   44   00000096: eba0                             JMP 0x38   45   00000098: ea   45   00000099: ebf5                             JMP 0x90   46   0000009b: dc   46   0000009c: eb09                             JMP 0xa7   47   0000009e: 81c388775b66                     ADD EBX, 0x665b7788   48   000000a4: ebaf                             JMP 0x55   49   000000a6: d8   49   000000a7: eb00                             JMP 0xa9   50   000000a9: eb05                             JMP 0xb0   51   000000ab: 74cb                             JZ 0x78   52   000000ad: eb21                             JMP 0xd0   53   000000af: d8   53   000000b0: 81c0dc665268                     ADD EAX, 0x685266dc   54   000000b6: eb09                             JMP 0xc1   55   000000b8: 81c396e99ded                     ADD EBX, 0xed9de996   56   000000be: eb9d                             JMP 0x5d   57   000000c0: 83   57   000000c1: eb09                             JMP 0xcc   58   000000c3: 81f14ffbe7e8                     XOR ECX, 0xe8e7fb4f   59   000000c9: ebbc                             JMP 0x87   60   000000cb: d8   60   000000cc: eb1d                             JMP 0xeb   61   000000ce: eb12                             JMP 0xe2   62   000000d0: 53                               PUSH EBX   63   000000d1: eb09                             JMP 0xdc   64   000000d3: 81eb3c8f5c2d                     SUB EBX, 0x2d5c8f3c   65   000000d9: ebc3                             JMP 0x9e   66   000000db: 81   66   000000dc: e93effffff                       JMP 0x1f   67   000000e1: e8   67   000000e2: 81c1e1c939fa                     ADD ECX, 0xfa39c9e1   68   000000e8: ebd9                             JMP 0xc3   69   000000ea: da   69   000000eb: e95cffffff                       JMP 0x4c   70   000000f0: 83   70   000000f1: eb00                             JMP 0xf3   71   000000f3: 5b                               POP EBX   72   000000f4: e94effffff                       JMP 0x47   73   000000f9: da   

Please keep in mind that this code is properly disassembled as we knew where the wild bytes are and we could filter them out during the disassembly process.

Unaware of the wild bytes, poor OllyDbg disassembles the code like this:

00390000   B8 37272A61      MOV EAX,612A2737   00390005   EB 3A            JMP SHORT 00390041   00390007   EB 2F            JMP SHORT 00390038   00390009   81C0 6DE78AE1    ADD EAX,E18AE76D   0039000F   EB 16            JMP SHORT 00390027   00390011   81E8 7DAF9577    SUB EAX,7795AF7D   00390017   E9 94000000      JMP 003900B0   0039001C   83EB 08          SUB EBX,8   0039001F   BB 0089C4E5      MOV EBX,E5C48900   00390024   EB 53            JMP SHORT 00390079   00390026   68 EBE8F7EB      PUSH EBF7E8EB   0039002B   0C 81            OR AL,81   0039002D  -E9 699DEC08      JMP 09259D9B   00390032   E9 AB000000      JMP 003900E2   00390037   8381 C038E2D5 F6 ADD DWORD PTR DS:[ECX+D5E238C0],-0A   0039003E  ^EB C9            JMP SHORT 00390009   00390040   D8EB             FSUBR ST,ST(3)   00390042   56               PUSH ESI   00390043   EB 4B            JMP SHORT 00390090   00390045   EB 05            JMP SHORT 0039004C   00390047  ^75 B7            JNZ SHORT 00390000   00390049   EB 2D            JMP SHORT 00390078   0039004B  -E9 B995F3FD      JMP FE2C9609   00390050   E5 EB            IN EAX,0EB                               ; I/O command   00390052   1AEB             SBB CH,BL   00390054   1381 EBEF03DB    ADC EAX,DWORD PTR DS:[ECX+DB03EFEB]   0039005A   D0EB             SHR BL,1   0039005C   0839             OR BYTE PTR DS:[ECX],BH   0039005E   D8E9             FSUBR ST,ST(1)   00390060   8F00             POP DWORD PTR DS:[EAX]   00390062   0000             ADD BYTE PTR DS:[EAX],AL   00390064   81EB 51EA01C8    SUB EBX,C801EA51   0039006A   EB 06            JMP SHORT 00390072   0039006C   D8EB             FSUBR ST,ST(3)   0039006E   BD 81EB1385      MOV EBP,8513EB81   00390073   C0EB 0C          SHR BL,0C   00390076   EB 01            JMP SHORT 00390079   00390078   C3               RETN   00390079   81EB EB56863B    SUB EBX,3B8656EB   0039007F   EB 52            JMP SHORT 003900D3   00390081   D8EB             FSUBR ST,ST(3)   00390083   27               DAA   00390084   F7EB             IMUL EBX   00390086   0981 E93CDBAC    OR DWORD PTR DS:[ECX+ACDB3CE9],EAX   0039008C   3F               AAS   0039008D  ^EB D9            JMP SHORT 00390068   0039008F   EA 81C0C757 B8D5 JMP FAR D5B8:57C7C081                    ; Far jump   00390096  ^EB A0            JMP SHORT 00390038   00390098   EA EBF5DCEB 0981 JMP FAR 8109:EBDCF5EB                    ; Far jump   0039009F   C3               RETN   003900A0   8877 5B          MOV BYTE PTR DS:[EDI+5B],DH   003900A3  -66:EB AF         JMP SHORT 00000055   003900A6   D8EB             FSUBR ST,ST(3)   003900A8   00EB             ADD BL,CH   003900AA   05 74CBEB21      ADD EAX,21EBCB74   003900AF   D881 C0DC6652    FADD DWORD PTR DS:[ECX+5266DCC0]   003900B5   68 EB0981C3      PUSH C38109EB   003900BA   96               XCHG EAX,ESI   003900BB  -E9 9DEDEB9D      JMP 9E24EE5D   003900C0   83EB 09          SUB EBX,9   003900C3   81F1 4FFBE7E8    XOR ECX,E8E7FB4F   003900C9  ^EB BC            JMP SHORT 00390087   003900CB   D8EB             FSUBR ST,ST(3)   003900CD   1D EB1253EB      SBB EAX,EB5312EB   003900D2   0981 EB3C8F5C    OR DWORD PTR DS:[ECX+5C8F3CEB],EAX   003900D8   2D EBC381E9      SUB EAX,E981C3EB   003900DD   3E:FFFF          ???                                      ; Unknown command   003900E0   FFE8             JMP FAR EAX                              ; Illegal use of register   003900E2   81C1 E1C939FA    ADD ECX,FA39C9E1   003900E8  ^EB D9            JMP SHORT 003900C3   003900EA   DAE9             FUCOMPP   003900EC   5C               POP ESP   003900ED   FFFF             ???                                      ; Unknown command   003900EF   FF83 EB005BE9    INC DWORD PTR DS:[EBX+E95B00EB]   003900F5   4E               DEC ESI   003900F6   FFFF             ???                                      ; Unknown command   003900F8   FFDA             CALL FAR EDX                             ; Illegal use of register   

Have fun reversing that!

This part, covering execution flow obfuscation feature, sums up the subject of x86 shellcode obfuscation. I have learned a lot in the process of doing this research and I hope I’ve managed to show you something new.

From this point forward I may start working on porting the Python obfuscator to C/C++ and develop a stand-alone x86 obfuscation library that may be later used in more advanced projects I have in mind.

Such obfuscation engine would make a strong foundation into development of software anti-cracking protections. My next research may be finding ways to translate x86 instructions into other CPU instructions and writing an emulator engine in assembly language that would emulate the translated virtual instructions in x86 CPU architecture. This is as far as I know the best way to protect your critical code from prying eyes.

The tool in its current form, should do perfectly fine in preventing AV software from detecting known shellcodes embedded in executable files. Metasploit payloads, though, require some work first as they are not fully prepared for obfuscation. Generated payloads assume their execution flow and length won’t be modified, so in order to decrease their size, fixed relative offsets are used.

Source code

As always, you can get the latest version of the tool on GitHub .

If you liked this project, you have suggestions or you just want to say hi, you can find me on Twitter @mrgretzky or Google+ .

Stay tuned for more posts!

EOF

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » X86 Shellcode Obfuscation – Part 3: Execution Flow Deception

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址