首页
学习
活动
专区
圈层
工具
发布
21 篇文章
1
《coredump问题原理探究》Linux x86版6.3节有成员变量的类coredump例子
2
《coredump问题原理探究》Linux x86版6.5节虚函数的coredump例子
3
《coredump问题原理探究》Linux x86版6.8节多继承coredump例子
4
《coredump问题原理探究》Linux x86版7.2节vector coredump例子
5
《coredump问题原理探究》Linux x86版7.4节List coredump例子
6
《coredump问题原理探究》Linux x86版7.6节 Map coredump例子
7
《coredump问题原理探究》Linux x86版5.9节C风格数据结构内存布局之联合体
8
《coredump问题原理探究》Linux x86版6.1节C++风格数据结构内存布局之无成员变量的类
9
《coredump问题原理探究》Linux x86版6.2节C++风格数据结构内存布局之有成员变量的类
10
《coredump问题原理探究》Linux x86版4.5节函数的逆向之coredump例子
11
《coredump问题原理探究》Linux x86版5.1节C风格数据结构内存布局之引言
12
《coredump问题原理探究》Linux x86版5.2节C风格数据结构内存布局之基本数据类型
13
《coredump问题原理探究》Linux x86版5.3节C风格数据结构内存布局之数组
14
《coredump问题原理探究》Linux x86版5.4节C风格数据结构内存布局之数组coredump例子
15
《coredump问题原理探究》Linux x86版5.5节C风格数据结构内存布局之基本数据类型构成的结构体
16
《coredump问题原理探究》Linux x86版5.6节C风格数据结构内存布局之复合类型构成的结构体
17
《coredump问题原理探究》Linux x86版5.7节C风格数据结构内存布局之结构体数组
18
《coredump问题原理探究》Linux x86版5.8节C风格数据结构内存布局之结构体数组结构体coredump
19
《coredump问题原理探究》Linux x86版3.5节栈布局之-fomit-frame-pointer编译选项
20
《coredump问题原理探究》Linux x86版3.6节栈布局之gcc内嵌关键字
21
《coredump问题原理探究》Linux x86版3.8节栈布局之栈溢出coredump例子

《coredump问题原理探究》Linux x86版4.5节函数的逆向之coredump例子

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1344515

在了解了上面的规律后,现在开始来解决本章一开头的问题:

代码语言:javascript
复制
(gdb) bt
#0  0x4365b569 in vfprintf () from /lib/libc.so.6
#1  0x436629ff in printf () from /lib/libc.so.6
#2  0x080485b9 in main ()

看一下main函数的汇编:

代码语言:javascript
复制
(gdb) disassemble main
Dump of assembler code for function main:
   0x08048500 <+0>:     push   %ebp
   0x08048501 <+1>:     mov    %esp,%ebp
   0x08048503 <+3>:     and    $0xfffffff0,%esp
   0x08048506 <+6>:     sub    $0x20,%esp
   0x08048509 <+9>:     movl   $0x0,0x1c(%esp)

   0x08048511 <+17>:    jmp    0x80485bf <main+191>

   0x08048516 <+22>:    mov    0x1c(%esp),%eax
   0x0804851a <+26>:    lea    0x0(,%eax,4),%edx
   0x08048521 <+33>:    mov    0xc(%ebp),%eax
   0x08048524 <+36>:    add    %edx,%eax
   0x08048526 <+38>:    mov    (%eax),%eax
   0x08048528 <+40>:    mov    %eax,(%esp)

   0x0804852b <+43>:    call   0x80483d0 <strlen@plt>

   0x08048530 <+48>:    mov    %eax,0x18(%esp)
   0x08048534 <+52>:    mov    0x18(%esp),%eax
   0x08048538 <+56>:    cmp    $0x1,%eax

   0x0804853b <+59>:    je     0x8048570 <main+112>

   0x0804853d <+61>:    cmp    $0x2,%eax

   0x08048540 <+64>:    je     0x804858f <main+143>

   0x08048542 <+66>:    test   %eax,%eax

   0x08048544 <+68>:    jne    0x80485a5 <main+165>

   0x08048546 <+70>:    mov    0x1c(%esp),%eax
   0x0804854a <+74>:    lea    0x0(,%eax,4),%edx
   0x08048551 <+81>:    mov    0xc(%ebp),%eax
   0x08048554 <+84>:    add    %edx,%eax
   0x08048556 <+86>:    mov    (%eax),%eax
   0x08048558 <+88>:    movzbl (%eax),%eax
   0x0804855b <+91>:    movsbl %al,%eax
   0x0804855e <+94>:    mov    %eax,0x4(%esp)
   0x08048562 <+98>:    movl   $0x8048674,(%esp)

   0x08048569 <+105>:   call   0x80483e0 <printf@plt>
   0x0804856e <+110>:   jmp    0x80485ba <main+186>

   0x08048570 <+112>:   mov    0x1c(%esp),%eax
   0x08048574 <+116>:   add    $0x1,%eax
   0x08048577 <+119>:   lea    0x0(,%eax,4),%edx
   0x0804857e <+126>:   mov    0xc(%ebp),%eax
   0x08048581 <+129>:   add    %edx,%eax
   0x08048583 <+131>:   mov    (%eax),%eax
   0x08048585 <+133>:   mov    %eax,(%esp)

   0x08048588 <+136>:   call   0x80483f0 <puts@plt>
   0x0804858d <+141>:   jmp    0x80485ba <main+186>

   0x0804858f <+143>:   mov    0x1c(%esp),%eax
   0x08048593 <+147>:   mov    %eax,0x4(%esp)
   0x08048597 <+151>:   movl   $0x8048678,(%esp)

   0x0804859e <+158>:   call   0x80483e0 <printf@plt>
   0x080485a3 <+163>:   jmp    0x80485ba <main+186>

   0x080485a5 <+165>:   mov    0x1c(%esp),%eax
   0x080485a9 <+169>:   mov    %eax,0x4(%esp)
   0x080485ad <+173>:   movl   $0x804867c,(%esp)

   0x080485b4 <+180>:   call   0x80483e0 <printf@plt>

   0x080485b9 <+185>:   nop
   0x080485ba <+186>:   addl   $0x1,0x1c(%esp)
   0x080485bf <+191>:   mov    0x1c(%esp),%eax
   0x080485c3 <+195>:   cmp    0x8(%ebp),%eax
   0x080485c6 <+198>:   setl   %al
   0x080485c9 <+201>:   test   %al,%al

   0x080485cb <+203>:   jne    0x8048516 <main+22>

   0x080485d1 <+209>:   mov    $0x0,%eax
   0x080485d6 <+214>:   leave  
   0x080485d7 <+215>:   ret    
End of assembler dump.

代码语言:javascript
复制
   0x080485cb <+203>:   jne    0x8048516 <main+22>

可知,0x8048516到0x080485cb构成一个循环。

而0x080486cb的判断条件:

代码语言:javascript
复制
   0x080485bf <+191>:   mov    0x1c(%esp),%eax
   0x080485c3 <+195>:   cmp    0x8(%ebp),%eax
   0x080485c6 <+198>:   setl   %al
   0x080485c9 <+201>:   test   %al,%al

里,提到ebp + 8.,由于main函数的原型第一个参数是argc,这在第三章“函数参数”有提及的。所以,上面的语句是判断esp+0x1c的值是否和argc相等,如果不是,就继续循环,否则跳出循环。假定esp+0x1c这个变量命名为cnt。

又由

代码语言:javascript
复制
   0x080485ba <+186>:   addl   $0x1,0x1c(%esp)

可翻译成 cnt++,可知,

cnt一开初应该是小于argc的。每次递增都要判断一次。

又由

代码语言:javascript
复制
   0x08048509 <+9>:     movl   $0x0,0x1c(%esp)
   0x08048511 <+17>:    jmp    0x80485bf <main+191>

可知,cnt的初始值为0,且一初始化之后就跳转到0x080485bf和argc比较。

而又由

代码语言:javascript
复制
   0x080485d1 <+209>:   mov    $0x0,%eax
   0x080485d6 <+214>:   leave  
   0x080485d7 <+215>:   ret    

结合第三章“返回值”那一节可知,main函数无论什么情况都返回0。

所以,整个main函数可以翻译成这样:

代码语言:javascript
复制
int main( int argc, char* argv[] )
{
	int cnt = 0;
while ( cnt < argc )
{
   0x08048516 <+22>:    mov    0x1c(%esp),%eax
   0x0804851a <+26>:    lea    0x0(,%eax,4),%edx
   0x08048521 <+33>:    mov    0xc(%ebp),%eax
   0x08048524 <+36>:    add    %edx,%eax
   0x08048526 <+38>:    mov    (%eax),%eax
   0x08048528 <+40>:    mov    %eax,(%esp)
   0x0804852b <+43>:    call   0x80483d0 <strlen@plt>
   0x08048530 <+48>:    mov    %eax,0x18(%esp)
   0x08048534 <+52>:    mov    0x18(%esp),%eax
   0x08048538 <+56>:    cmp    $0x1,%eax
   0x0804853b <+59>:    je     0x8048570 <main+112>
   0x0804853d <+61>:    cmp    $0x2,%eax
   0x08048540 <+64>:    je     0x804858f <main+143>
   0x08048542 <+66>:    test   %eax,%eax
   0x08048544 <+68>:    jne    0x80485a5 <main+165>
   0x08048546 <+70>:    mov    0x1c(%esp),%eax
   0x0804854a <+74>:    lea    0x0(,%eax,4),%edx
   0x08048551 <+81>:    mov    0xc(%ebp),%eax
   0x08048554 <+84>:    add    %edx,%eax
   0x08048556 <+86>:    mov    (%eax),%eax
   0x08048558 <+88>:    movzbl (%eax),%eax
   0x0804855b <+91>:    movsbl %al,%eax
   0x0804855e <+94>:    mov    %eax,0x4(%esp)
   0x08048562 <+98>:    movl   $0x8048674,(%esp)
   0x08048569 <+105>:   call   0x80483e0 <printf@plt>
   0x0804856e <+110>:   jmp    0x80485ba <main+186>
   0x08048570 <+112>:   mov    0x1c(%esp),%eax
   0x08048574 <+116>:   add    $0x1,%eax
   0x08048577 <+119>:   lea    0x0(,%eax,4),%edx
   0x0804857e <+126>:   mov    0xc(%ebp),%eax
   0x08048581 <+129>:   add    %edx,%eax
   0x08048583 <+131>:   mov    (%eax),%eax
   0x08048585 <+133>:   mov    %eax,(%esp)
   0x08048588 <+136>:   call   0x80483f0 <puts@plt>
   0x0804858d <+141>:   jmp    0x80485ba <main+186>
   0x0804858f <+143>:   mov    0x1c(%esp),%eax
   0x08048593 <+147>:   mov    %eax,0x4(%esp)
   0x08048597 <+151>:   movl   $0x8048678,(%esp)
   0x0804859e <+158>:   call   0x80483e0 <printf@plt>
   0x080485a3 <+163>:   jmp    0x80485ba <main+186>
   0x080485a5 <+165>:   mov    0x1c(%esp),%eax
   0x080485a9 <+169>:   mov    %eax,0x4(%esp)
   0x080485ad <+173>:   movl   $0x804867c,(%esp)
   0x080485b4 <+180>:   call   0x80483e0 <printf@plt>
   0x080485b9 <+185>:   nop
	
	cnt++;
}

return 0;
}

分析一下上面汇编块的跳转语句

代码语言:javascript
复制
   0x08048538 <+56>:    cmp    $0x1,%eax
   0x0804853b <+59>:    je     0x8048570 <main+112>

可知,

代码语言:javascript
复制
   0x08048570 <+112>:   mov    0x1c(%esp),%eax
   0x08048574 <+116>:   add    $0x1,%eax
   0x08048577 <+119>:   lea    0x0(,%eax,4),%edx
   0x0804857e <+126>:   mov    0xc(%ebp),%eax
   0x08048581 <+129>:   add    %edx,%eax
   0x08048583 <+131>:   mov    (%eax),%eax
   0x08048585 <+133>:   mov    %eax,(%esp)
   0x08048588 <+136>:   call   0x80483f0 <puts@plt>
   0x0804858d <+141>:   jmp    0x80485ba <main+186>

是在eax等于1的情况下的代码块。

同样分析

代码语言:javascript
复制
   0x0804853d <+61>:    cmp    $0x2,%eax
   0x08048540 <+64>:    je     0x804858f <main+143>
   0x08048542 <+66>:    test   %eax,%eax
   0x08048544 <+68>:    jne    0x80485a5 <main+165>

可知

代码语言:javascript
复制
   0x0804858f <+143>:   mov    0x1c(%esp),%eax
   0x08048593 <+147>:   mov    %eax,0x4(%esp)
   0x08048597 <+151>:   movl   $0x8048678,(%esp)
   0x0804859e <+158>:   call   0x80483e0 <printf@plt>
   0x080485a3 <+163>:   jmp    0x80485ba <main+186>

是在eax为1的情况下的代码块。

代码语言:javascript
复制
   0x08048546 <+70>:    mov    0x1c(%esp),%eax
   0x0804854a <+74>:    lea    0x0(,%eax,4),%edx
   0x08048551 <+81>:    mov    0xc(%ebp),%eax
   0x08048554 <+84>:    add    %edx,%eax
   0x08048556 <+86>:    mov    (%eax),%eax
   0x08048558 <+88>:    movzbl (%eax),%eax
   0x0804855b <+91>:    movsbl %al,%eax
   0x0804855e <+94>:    mov    %eax,0x4(%esp)
   0x08048562 <+98>:    movl   $0x8048674,(%esp)
   0x08048569 <+105>:   call   0x80483e0 <printf@plt>
   0x0804856e <+110>:   jmp    0x80485ba <main+186>

是在eax为0的情况下的代码块。

代码语言:javascript
复制
   0x080485a5 <+165>:   mov    0x1c(%esp),%eax
   0x080485a9 <+169>:   mov    %eax,0x4(%esp)
   0x080485ad <+173>:   movl   $0x804867c,(%esp)
   0x080485b4 <+180>:   call   0x80483e0 <printf@plt>
   0x080485b9 <+185>:   nop

是在eax不为0,1,2这三种情况下的代码块。由于这几个判断都是特定的整数,所以最好用switch结构来还原。

而对于

代码语言:javascript
复制
   0x0804856e <+110>:   jmp    0x80485ba <main+186>

这条指令所跳转的地方,刚好是

代码语言:javascript
复制
   0x080485ba <+186>:   addl   $0x1,0x1c(%esp)

即cnt++;

而eax的则是由

代码语言:javascript
复制
   0x0804852b <+43>:    call   0x80483d0 <strlen@plt>
   0x08048530 <+48>:    mov    %eax,0x18(%esp)
   0x08048534 <+52>:    mov    0x18(%esp),%eax

得来的,根据第三章“返回值”那一节,可以知道eax应该是strlen函数的返回值,命名为len。

而在

代码语言:javascript
复制
   0x08048516 <+22>:    mov    0x1c(%esp),%eax
   0x0804851a <+26>:    lea    0x0(,%eax,4),%edx
   0x08048521 <+33>:    mov    0xc(%ebp),%eax
   0x08048524 <+36>:    add    %edx,%eax
   0x08048526 <+38>:    mov    (%eax),%eax
   0x08048528 <+40>:    mov    %eax,(%esp)

由于esp+0x1c已经命名为cnt了,ebp+0xC为main函数第二个参数argv。那么这一段汇编的意思是取argvcnt的值,并把它压入栈里。

所以main函数又会变成这样:

代码语言:javascript
复制
int main( int argc, char* argv[] )
{
	int cnt = 0;
while ( cnt < argc )
{
	size_t len = strlen( argv[cnt] );
	switch ( len )
	{
		case 0:
        {
   			0x08048546 <+70>:    mov    0x1c(%esp),%eax
            0x0804854a <+74>:    lea    0x0(,%eax,4),%edx
  0x08048551 <+81>:    mov    0xc(%ebp),%eax
  0x08048554 <+84>:    add    %edx,%eax
  0x08048556 <+86>:    mov    (%eax),%eax
  0x08048558 <+88>:    movzbl (%eax),%eax
  0x0804855b <+91>:    movsbl %al,%eax
  0x0804855e <+94>:    mov    %eax,0x4(%esp)
  0x08048562 <+98>:    movl   $0x8048674,(%esp)
  0x08048569 <+105>:   call   0x80483e0 <printf@plt>
  break;
}
case 1:
{
   0x08048570 <+112>:   mov    0x1c(%esp),%eax
   0x08048574 <+116>:   add    $0x1,%eax
   0x08048577 <+119>:   lea    0x0(,%eax,4),%edx
   0x0804857e <+126>:   mov    0xc(%ebp),%eax
   0x08048581 <+129>:   add    %edx,%eax
   0x08048583 <+131>:   mov    (%eax),%eax
   0x08048585 <+133>:   mov    %eax,(%esp)
   0x08048588 <+136>:   call   0x80483f0 <puts@plt>
   break;
}
case 2:
{
   0x0804858f <+143>:   mov    0x1c(%esp),%eax
   0x08048593 <+147>:   mov    %eax,0x4(%esp)
   0x08048597 <+151>:   movl   $0x8048678,(%esp)
   0x0804859e <+158>:   call   0x80483e0 <printf@plt>
break;
			  }
			  default:
			  {
	 0x080485a5 <+165>:   mov    0x1c(%esp),%eax
   0x080485a9 <+169>:   mov    %eax,0x4(%esp)
   0x080485ad <+173>:   movl   $0x804867c,(%esp)
   0x080485b4 <+180>:   call   0x80483e0 <printf@plt>
   0x080485b9 <+185>:   nop
   break;
}
   		}
cnt++;
}

return 0;
}

看一下case 0情况的汇编:

代码语言:javascript
复制
  0x08048546 <+70>:    mov    0x1c(%esp),%eax
  0x0804854a <+74>:    lea    0x0(,%eax,4),%edx
  0x08048551 <+81>:    mov    0xc(%ebp),%eax
  0x08048554 <+84>:    add    %edx,%eax
  0x08048556 <+86>:    mov    (%eax),%eax
  0x08048558 <+88>:    movzbl (%eax),%eax
  0x0804855b <+91>:    movsbl %al,%eax
  0x0804855e <+94>:    mov    %eax,0x4(%esp)
  0x08048562 <+98>:    movl   $0x8048674,(%esp)
  0x08048569 <+105>:   call   0x80483e0 <printf@plt>

由于printf的第一个参数是格式字符串,那么看一下0x8048674存放着什么内容:

代码语言:javascript
复制
(gdb) x /s 0x8048674
0x8048674 <__dso_handle+4>:      "%c\n"

代码语言:javascript
复制
  0x08048546 <+70>:    mov    0x1c(%esp),%eax
  0x0804854a <+74>:    lea    0x0(,%eax,4),%edx
  0x08048551 <+81>:    mov    0xc(%ebp),%eax
  0x08048554 <+84>:    add    %edx,%eax

又在分析strlen时已经知道是指argvcnt+1,所以,

代码语言:javascript
复制
  0x08048556 <+86>:    mov    (%eax),%eax
  0x08048558 <+88>:    movzbl (%eax),%eax
  0x0804855b <+91>:    movsbl %al,%eax

就是取argvcnt+1的值。

那么,case 0的汇编可翻译成

代码语言:javascript
复制
printf( “%c\n”, argv[cnt][0] );

也就是说,main函数可以变成这样:

代码语言:javascript
复制
int main( int argc, char* argv[] )
{
	int cnt = 0;
while ( cnt < argc )
{
	size_t len = strlen( argv[cnt] );
	switch ( len )
	{
		case 0:
        {
printf( “%c\n”, argv[cnt][0] );
  break;
}
case 1:
{
   0x08048570 <+112>:   mov    0x1c(%esp),%eax
   0x08048574 <+116>:   add    $0x1,%eax
   0x08048577 <+119>:   lea    0x0(,%eax,4),%edx
   0x0804857e <+126>:   mov    0xc(%ebp),%eax
   0x08048581 <+129>:   add    %edx,%eax
   0x08048583 <+131>:   mov    (%eax),%eax
   0x08048585 <+133>:   mov    %eax,(%esp)
   0x08048588 <+136>:   call   0x80483f0 <puts@plt>
   break;
}
case 2:
{
   0x0804858f <+143>:   mov    0x1c(%esp),%eax
   0x08048593 <+147>:   mov    %eax,0x4(%esp)
   0x08048597 <+151>:   movl   $0x8048678,(%esp)
   0x0804859e <+158>:   call   0x80483e0 <printf@plt>
break;
			  }
			  default:
			  {
   0x080485a5 <+165>:   mov    0x1c(%esp),%eax
   0x080485a9 <+169>:   mov    %eax,0x4(%esp)
   0x080485ad <+173>:   movl   $0x804867c,(%esp)
   0x080485b4 <+180>:   call   0x80483e0 <printf@plt>
   0x080485b9 <+185>:   nop
   break;
}
   		}
cnt++;
}

return 0;
}

按照case 0的情况可以分析出其它几个如下:

代码语言:javascript
复制
case 1:
{
puts( argv[cnt+1] );
   break;
}
case 2:
{
printf( “%d\n”, cnt );
break;
			  }
             default:
             {
                printf( “%s\n”, cnt );
                break;
             }

即整个main函数如下:

代码语言:javascript
复制
int main( int argc, char* argv[] )
{
	int cnt = 0;
while ( cnt < argc )
{
	size_t len = strlen( argv[cnt+1] );
	switch ( len )
	{
		case 0:
        {
printf( “%c\n”, argv[cnt+1][0] );
  break;
}
case 1:
{
puts( argv[cnt+2] );
   break;
}
case 2:
{
printf( “%d\n”, cnt );
break;
			  }
             default:
             {
                printf( “%s\n”, cnt );
                break;
             }
   		}
cnt++;
}

return 0;
}

由于崩溃指令地址0x080485b9是在一段中

代码语言:javascript
复制
			  default:
			  {
	 0x080485a5 <+165>:   mov    0x1c(%esp),%eax
   0x080485a9 <+169>:   mov    %eax,0x4(%esp)
   0x080485ad <+173>:   movl   $0x804867c,(%esp)
   0x080485b4 <+180>:   call   0x80483e0 <printf@plt>
   0x080485b9 <+185>:   nop
   break;
}

可知,是由

代码语言:javascript
复制
             default:
             {
                printf( “%s\n”, cnt );
                break;
             }

导致崩溃的。

对比一下源代码,可见非常吻合。

代码语言:javascript
复制
 #include <stdio.h>
 #include <string.h>
 int main(int argc, char* argv[] )
 {
     for ( int i = 0; i < argc; i++ )
     {
         int len = strlen( argv[i] );
         switch ( len )
         {
             case 0:
                 printf( "%c\n", argv[i][0] );
                 break;
             case 1:
                 printf( "%s\n", argv[i+1] );
                 break;
             case 2:
                 printf( "%d\n", i );
                 break;
             default:
                 printf( "%s\n", i );
                 break;
         }
     }
 
     return 0;
 }
下一篇
举报
领券