先看代码:
pop.c
extern int stack[512];
extern int top;
int pop()
{
if(top<=0)
return;
top--;
return stack[top];
}
push.c
extern int stack[512];
extern int top;
void push(int v)
{
if(top>=512)
return;
stack[top]=v;
top++;
}
main.c
#include <stdio.h>
int stack[512];
int top=0;
void main(int n,void * a[])
{
push(1);
push(2);
int d=pop();
printf("%d\n",d);
}
正常编译
正常编译时使用gcc -c -g pop.c push.c main.c,然后使用objdump -ds 查看反汇编:
objdump -dS pop.o
int pop()
{
0:55 push %ebp
1:89 e5 mov %esp,%ebp
if(top<=0)
3:a1 00 00 00 00 mov 0x0,%eax
8:85 c0 test %eax,%eax
a:7f 02 jg e <pop+0xe>
c:eb 19 jmp 27 <pop+0x27>
return;
top--;
e:a1 00 00 00 00 mov 0x0,%eax
13:83 e8 01 sub $0x1,%eax
16:a3 00 00 00 00 mov %eax,0x0
return stack[top];
1b:a1 00 00 00 00 mov 0x0,%eax
20:8b 04 85 00 00 00 00 mov 0x0(,%eax,4),%eax
}
27:5d pop %ebp
28:c3 ret
可以看出top的访问地址都是0。
使用gcc pop.o push.o main.o -o main,然后使用 objdump -dS main查看反汇编
extern int top;
int pop()
{
8048428: 55 push %ebp
8048429: 89 e5 mov %esp,%ebp
if(top<=0)
804842b: a1 28 a0 04 08 mov 0x804a028,%eax
8048430: 85 c0 test %eax,%eax
8048432: 7f 02 jg 8048436 <pop+0xe>
8048434: eb 19 jmp 804844f <pop+0x27>
return;
top--;
8048436: a1 28 a0 04 08 mov 0x804a028,%eax
804843b: 83 e8 01 sub $0x1,%eax
804843e: a3 28 a0 04 08 mov %eax,0x804a028
return stack[top];
8048443: a1 28 a0 04 08 mov 0x804a028,%eax
8048448: 8b 04 85 40 a0 04 08 mov 0x804a040(,%eax,4),%eax
}
可以看到top的地址已经被硬编码为0x804a028。
位置无关编译
在编译动态链接库时,一般需要添加fPIC编译选项,例如:
gcc -g -fPIC -c pop.c push.c,使用objdump -dS pop.o查看反汇编
extern int stack[512];
extern int top;
int pop()
{
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: e8 fc ff ff ff call 4 <pop+0x4>
8: 81 c1 02 00 00 00 add $0x2,%ecx
if(top<=0)
e: 8b 81 00 00 00 00 mov 0x0(%ecx),%eax
14: 8b 00 mov (%eax),%eax
16: 85 c0 test %eax,%eax
18: 7f 02 jg 1c <pop+0x1c>
1a: eb 24 jmp 40 <pop+0x40>
return;
top--;
1c: 8b 81 00 00 00 00 mov 0x0(%ecx),%eax
22: 8b 00 mov (%eax),%eax
24: 8d 50 ff lea -0x1(%eax),%edx
27: 8b 81 00 00 00 00 mov 0x0(%ecx),%eax
2d: 89 10 mov %edx,(%eax)
return stack[top];
2f: 8b 81 00 00 00 00 mov 0x0(%ecx),%eax
35: 8b 10 mov (%eax),%edx
37: 8b 81 00 00 00 00 mov 0x0(%ecx),%eax
3d: 8b 04 90 mov (%eax,%edx,4),%eax
}
上面访问top需要经过2步骤:
eax = ecx[0]
eax = exa[0]
和一般编译方式不同,它使用的间接地址访问top数据。
将这些对象文件链接成为动态库
gcc -shared -o libstack.so push.o pop.o,然后察看libstack.so的反汇编
000004b8 <pop>:
extern int stack[512];
extern int top;
int pop()
{
4b8: 55 push %ebp
4b9: 89 e5 mov %esp,%ebp
4bb: e8 3a 00 00 00 call 4fa <__i686.get_pc_thunk.cx>
4c0: 81 c1 34 1b 00 00 add $0x1b34,%ecx
if(top<=0)
4c6: 8b 81 f4 ff ff ff mov -0xc(%ecx),%eax
4cc: 8b 00 mov (%eax),%eax
4ce: 85 c0 test %eax,%eax
4d0: 7f 02 jg 4d4 <pop+0x1c>
4d2: eb 24 jmp 4f8 <pop+0x40>
return;
top--;
4d4: 8b 81 f4 ff ff ff mov -0xc(%ecx),%eax
4da: 8b 00 mov (%eax),%eax
4dc: 8d 50 ff lea -0x1(%eax),%edx
4df: 8b 81 f4 ff ff ff mov -0xc(%ecx),%eax
4e5: 89 10 mov %edx,(%eax)
return stack[top];
4e7: 8b 81 f4 ff ff ff mov -0xc(%ecx),%eax
4ed: 8b 10 mov (%eax),%edx
4ef: 8b 81 fc ff ff ff mov -0x4(%ecx),%eax
4f5: 8b 04 90 mov (%eax,%edx,4),%eax
}
比较pop.o可以看出:
top的地址存储位置由原来的ecx[0]改变为ecx[-12]
另外也能看出stack的地址存储位置由原来的ecx[0]改变为ecx[-4]
编译可执行文件并执行
htm@htm:~/test/testlink$ gcc main.o -lstack -o main -L.
htm@htm:~/test/testlink$ ./main
./main: error while loading shared libraries: libstack.so: cannot open shared object file: No such file or directory
编译时指定了libstack.so的查找路径,但执行时却找不到这个文件。
ldd命令能够模拟程序执行,查找需要的动态链接文件。
htm@htm:~/test/testlink$ ldd main
linux-gate.so.1 => (0xb77d5000)
libstack.so => not found
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7614000)
/lib/ld-linux.so.2 (0xb77d6000)
对于找不到动态库可以修改LD_LIBRARY_PATH环境变量或者/etc/ld.so.conf文件,也可以将路径直接写入可执行文件中:
htm@htm:~/test/testlink$ gcc -lstack -L. main.c -o main -Wl,-rpath,/home/htm/test/testlink/
htm@htm:~/test/testlink$ ldd main
linux-gate.so.1 => (0xb77d6000)
libstack.so => /home/htm/test/testlink/libstack.so (0xb77d1000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7612000)
/lib/ld-linux.so.2 (0xb77d7000)
注意-L只能告诉编译器在哪里找到库文件,但是运行时库文件在哪里需要环境变量或者配置文件或者查找执行文件中的rpath
使用Wl,rpath后,利用readelf可以查看到main文件中多了一些信息
htm@htm:~/test/testlink$ readelf -d main
Dynamic section at offset 0xf18 contains 22 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libstack.so]
0x00000001 (NEEDED) Shared library: [libc.so.6]
<span style="color:#ff0000;">0x0000000f (RPATH) Library rpath: [/home/htm/test/testlink/]</span>
0x0000000c (INIT) 0x804840c
0x0000000d (FINI) 0x804864c
0x6ffffef5 (GNU_HASH) 0x80481ac
0x00000005 (STRTAB) 0x80482e0
0x00000006 (SYMTAB) 0x80481f0
0x0000000a (STRSZ) 188 (bytes)
0x0000000b (SYMENT) 16 (bytes)
0x00000015 (DEBUG) 0x0
0x00000003 (PLTGOT) 0x8049ff4
0x00000002 (PLTRELSZ) 40 (bytes)
0x00000014 (PLTREL) REL
0x00000017 (JMPREL) 0x80483e4
0x00000011 (REL) 0x80483dc
0x00000012 (RELSZ) 8 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffe (VERNEED) 0x80483bc
0x6fffffff (VERNEEDNUM) 1
0x6ffffff0 (VERSYM) 0x804839c
0x00000000 (NULL) 0x0
使用这种技术,可以为指定程序添加后门。
动态链接库运行过程
程序运行时,遇到动态库提供的接口或者变量,程序会跳转到动态连接器/lib/ld-linux.so.2,在其中完成动态链接的过程,并调用或者访问变量。