这是2008年写的文章, 没有正式发表过,里面使用的环境不记得是i386Linux还是AMD64的Linux了。
其实,叫 bincode 合适一点,一些搞入侵的人会称之为shellcode,本文的目的是探索与学习,不是为了搞入侵,实用性也不强。
先实现一个简单的装载的代码,通常叫做 launcher。
#include<stdio.h> #include<sys/stat.h> #include<sys/types.h> #include<sys/mman.h> #include<sys/fcntl.h> #include<unistd.h> static int file_size ( int fd ) { struct stat buf; if ( fd<0 ) return -1; fstat (fd,&buf); if ( buf.st_size<0 ) return -2; return buf.st_size; } static int read_file( const char *filename, void *buf ) { int rfd; size_t buflen = 0; rfd = open(filename,O_RDONLY,S_IRUSR|S_IWUSR); if ( -1==rfd ) return -3; buflen = file_size ( rfd ); if ( buflen<=0 ) return -4; read(rfd,buf,buflen); close(rfd); return 0; } int (*myfunc)(char *str); /*定义一个函数指针*/ int main(int argc,char *argv[] ) { myfunc = malloc( 1024 * sizeof(char*) ); /*申请一小段内存,把指针指向这段内存,作为缓冲区用*/ read_file( "./myfunc.bin", myfunc ); /*把myfunc.bin读进来*/ int i = 0; i = myfunc("AABBCC"); /*执行*/ printf("i=%d\n",i); }
gcc -g -o binloader binloader.c;
然后,实现 myfunc.bin
#include <stdio.h> #include <sys/types.h> #include <sys/uio.h> #include <unistd.h> int bin_printf ( char *instr ) { char *word = "hello "; write( 1,word, strlen(word) ); write( 1,instr, strlen(instr) ); write( 1,"\n", 1 ); return 0x0; }
代码很简单了,关键之处,在于怎么编译:
gcc -fpic -c myfunc.c; ld myfunc.o -static -o myfunc -e bin_printf -lc objcopy -R .note -R .comment -S -O binary myfunc myfunc.bin
然后执行
./binloader %./binloader hello AABBCC i=0
可见,这是很完整的一个函数调用的过程,解释下编译的几个步骤:
gcc -fpic -c myfunc.c;
-fpic 是让 gcc 生产的汇编代码是位置无关的,否则,
调用
write( 1,word, strlen(word) );
的时候,对 word 的寻址就通常无法正确。
ld myfunc.o -static -o myfunc -e bin_printf -lc
-e bin_printf
链接时,入口指定是 bin_printf
因为用到一个系统调用wrie,所以 -lc
最后就是用
objcopy -R .note -R .comment -S -O binary myfunc myfunc.bin
产生一个bin格式的文件