首页 > 编程知识 正文

csrf攻击的底层实现原理,单点登录实现原理

时间:2023-05-04 10:26:57 阅读:181686 作者:1178

显示函数调用关系(backtrace/callstack )是调试器所需的功能之一,例如,在gdb中可以通过bt命令看到backtrace。 程序崩溃时,函数调用关系有助于快速找出问题的根源,了解其实现原理,可以扩展自己的知识面,在没有调试器的情况下,也可以实现自己的后台。 更重要的是,分析backtrace的实现原理很有意思。 现在让我们一起研究一下:

glibc提供了backtrace函数,可帮助您获取当前函数的backtrace。 先看看它的用法,然后模仿它写吧。

#include stdio.h

#include stdlib.h

#include execinfo.h

#define MAX_LEVEL 4

静态语音测试2 (

{

int i=0;

void* buffer[MAX_LEVEL]={0};

intsize=backtrace(buffer,MAX_LEVEL ); for(I=0; i size; I ) printf(calledby%p/n ),buffer[i]; } return; () ) ) ) )。

静态语音测试1 (

{

int a=0x11111111;

int b=0x11111112;

test2(; a=b; 返回; () ) ) ) )。

静态语音测试(

{

int a=0x10000000;

int b=0x10000002;

test1(; a=b; 返回; () ) ) ) )。

intmain(intargc,char* argv[] ) ) ) ) ) ) ) ) ) int main ) ) ) int main ) ) ) int argc,char* argv[] ) ) ) ) ) ) ) int

{

test (;

返回0; () ) ) ) )。

编译并运行:

gcc -g -Wall bt_std.c -o bt_std

./bt_std

丝网印刷:

called by 08048440

called by 0804848a

called by 080484ab

called by 080484c9

上面印的是调用者的地址,对程序员来说不太直观。 glibc还提供了另一个函数backtrace_symbols,可以将这些地址转换为源代码位置(通常为函数名称)。 但是,这个函数不太方便,特别是在没有调试信息的情况下,几乎得不到有用的信息。 这里使用另一个工具addr2line来实现地址到源代码位置的转换。

执行:

./Bt _ STD|awk‘{ print“addr2line”“$3"-ebt _ STD”}’t.sh; t.sh; rm -f t.sh

丝网印刷:

/home/work/mine/sys Prog/think-in-comp way/backtrace/Bt _ STD.c :12

/home/work/mine/sys Prog/think-in-comp way/backtrace/Bt _ STD.c :28

/home/work/mine/sys Prog/think-in-comp way/backtrace/Bt _ STD.c :39

/home/work/mine/sys Prog/think-in-comp way/backtrace/Bt _ STD.c :48

backtrace是怎么实现的? 在x86计算机上,调用函数时,堆栈中的数据结构可能如下:

参数n

自变量…函数自变量被堆栈的顺序与具体的调用方法有关

参数3

参数2

参数1 EIP完成这次调用后,下一个命令的地址

EBP保存调用方的EBP,EBP指向此时的堆栈顶部。

---------新的EBP指向这里

临时变量1

临时变量2

临时变量3

临时变量…

临时变量5 (说明:下面是地址,上面是地址,堆栈向下延伸) ) )。

调用时,首先将被调函数的参数推入堆栈。 c语言的堆栈方法是先按下最后一个参数,再按下倒数第二个参数的顺序将其推入堆栈,最后按下第一个参数。

然后按下EIP和EBP。 此时,EIP指向本次调用完成后的下一个指令的地址。 该地址可以与函数调用方的地址近似。 EBP是调用方与被调函数的分界线,分界线上有调用方的临时变量、被调函数的参数、函数返回地址(EIP )、以及上位函数的EBP,分界线下有被调函数的临时变量。

最后进入被调制函数,对其分配临时变量的空间。 gcc的处理因版本而异,对于gcc3.4等旧版本的gcc,第一个临时变量位于最高地址,第二个按顺序分布。 在gcc4.3等新版本的gcc中,临时变量的位置相反。 这意味着最后一个临时变量位于最高地址,按倒数第二的顺序分布。

为了实现后退

1.获取当前函数的EBP。
2.通过EBP获得调用者的EIP。
3.通过EBP获得上一级的EBP。
4.重复这个过程,直到结束。

通过嵌入汇编代码,我们可以获得当前函数的EBP,不过这里我们不用汇编,而且通过临时变量的地址来获得当前函数的EBP。我们知道,对于gcc3.4生成的代码,当前函数的第一个临时变量的下一个位置就是EBP。而对于gcc4.3生成的代码,当前函数的最后一个临时变量的下一个位置就是EBP。

有了这些背景知识,我们来实现自己的backtrace:

#ifdef NEW_GCC
#define OFFSET 4
#else
#define OFFSET 0
#endif/NEW_GCC/

int backtrace(void** buffer, int size)
{
int n = 0xfefefefe;
int* p = &n;
int i = 0;

int ebp = p[1 + OFFSET];int eip = p[2 + OFFSET]; for(i = 0; i < size; i++){ buffer[i] = (void*)eip; p = (int*)ebp; ebp = p[0]; eip = p[1];} return size;

}

对于老版本的gcc,OFFSET定义为0,此时p+1就是EBP,而p[1]就是上一级的EBP,p[2]是调用者的EIP。本函数总共有5个int的临时变量,所以对于新版本gcc, OFFSET定义为5,此时p+5就是EBP。在一个循环中,重复取上一层的EBP和EIP,最终得到所有调用者的EIP,从而实现了backtrace。

现在我们用完整的程序来测试一下(bt.c):

#include <stdio.h>

#define MAX_LEVEL 4
#ifdef NEW_GCC
#define OFFSET 4
#else
#define OFFSET 0
#endif/NEW_GCC/

int backtrace(void** buffer, int size)
{
int n = 0xfefefefe;
int* p = &n;
int i = 0;

int ebp = p[1 + OFFSET];int eip = p[2 + OFFSET]; for(i = 0; i < size; i++){ buffer[i] = (void*)eip; p = (int*)ebp; ebp = p[0]; eip = p[1];} return size;

}

static void test2()
{
int i = 0;
void* buffer[MAX_LEVEL] = {0};

backtrace(buffer, MAX_LEVEL); for(i = 0; i < MAX_LEVEL; i++){ printf("called by %p/n", buffer[i]);} return;

}

static void test1()
{
int a=0x11111111;
int b=0x11111112;

test2();a = b; return;

}

static void test()
{
int a=0x10000000;
int b=0x10000002;

test1();a = b; return;

}

int main(int argc, char* argv[])
{
test();

return 0;

}

写个简单的Makefile:

CFLAGS=-g -Wall
all:
gcc34 $(CFLAGS) bt.c -o bt34
gcc $(CFLAGS) -DNEW_GCC bt.c -o bt
gcc $(CFLAGS) bt_std.c -o bt_std

clean:
rm -f bt bt34 bt_std

编译然后运行:
make
./bt|awk ‘{print “addr2line “$3″ -e bt”}’>t.sh;. t.sh;

屏幕打印:
/home/work/mine/sysprog/think-in-compway/backtrace/bt.c:37
/home/work/mine/sysprog/think-in-compway/backtrace/bt.c:51
/home/work/mine/sysprog/think-in-compway/backtrace/bt.c:62
/home/work/mine/sysprog/think-in-compway/backtrace/bt.c:71

对于可执行文件,这种方法工作正常。对于共享库,addr2line无法根据这个地址找到对应的源代码位置了。原因是:addr2line只能通过地址偏移量来查找,而打印出的地址是绝对地址。由于共享库加载到内存的位置是不确定的,为了计算地址偏移量,我们还需要进程maps文件的帮助:

通过进程的maps文件(/proc/进程号/maps),我们可以找到共享库的加载位置,如:

00c5d000-00c5e000 r-xp 00000000 08:05 2129013 /home/work/mine/sysprog/think-in-compway/backtrace/libbt_so.so
00c5e000-00c5f000 rw-p 00000000 08:05 2129013 /home/work/mine/sysprog/think-in-compway/backtrace/libbt_so.so

libbt_so.so的代码段加载到0×00c5d000-0×00c5e000,而backtrace打印出的地址是:
called by 0xc5d4eb
called by 0xc5d535
called by 0xc5d556
called by 0×80484ca

这里可以用打印出的地址减去加载的地址来计算偏移量。如,用 0xc5d4eb减去加载地址0×00c5d000,得到偏移量0×4eb,然后把0×4eb传给addr2line:

addr2line 0×4eb -f -s -e ./libbt_so.so

屏幕打印:
/home/work/mine/sysprog/think-in-compway/backtrace/bt_so.c:38

栈里的数据很有意思,在上一节中,通过分析栈里的数据,我们了解了变参函数的实现原理。在这一节中,通过分析栈里的数据,我们又学到了backtrace的实现原理。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。