首页
社区
课程
招聘
[旧帖] [分享]简单的函数逆向(还原) 0.00雪花
发表于: 2011-9-2 01:15 5532

[旧帖] [分享]简单的函数逆向(还原) 0.00雪花

2011-9-2 01:15
5532
看过论坛的一些关于逆向的贴子
说说我的看法:逆向工程并不是对代码进行分析就算了,实际上要对工程进行一定的还原(源代码形式),当然难度挺大,要对算法非常熟悉。要系统的数据结构非常熟悉,才有能力推还原工程的数据结构。

这里仅对一个简短的函数进行还原,当然并不可能还原为原作者源代码,是还原为在函数的逻辑思想上最接近的形式。
ntdll!memcpy_s:
00000000`77abee8c 48895c2408      mov     qword ptr [rsp+8],rbx
00000000`77abee91 4889742410      mov     qword ptr [rsp+10h],rsi
00000000`77abee96 57              push    rdi
00000000`77abee97 4883ec30        sub     rsp,30h
00000000`77abee9b 498bd9          mov     rbx,r9
00000000`77abee9e 498bf0          mov     rsi,r8
00000000`77abeea1 488bfa          mov     rdi,rdx
00000000`77abeea4 4d85c9          test    r9,r9
00000000`77abeea7 7504            jne     ntdll!memcpy_s+0x21 (00000000`77abeead)

ntdll!memcpy_s+0x1d:
00000000`77abeea9 33c0            xor     eax,eax
00000000`77abeeab eb1c            jmp     ntdll!memcpy_s+0x3d (00000000`77abeec9)

ntdll!memcpy_s+0x21:
00000000`77abeead 4885c9          test    rcx,rcx
00000000`77abeeb0 7527            jne     ntdll!memcpy_s+0x4d (00000000`77abeed9)

ntdll!memcpy_s+0x26:
00000000`77abeeb2 48214c2420      and     qword ptr [rsp+20h],rcx
00000000`77abeeb7 4533c9          xor     r9d,r9d
00000000`77abeeba 4533c0          xor     r8d,r8d
00000000`77abeebd 33d2            xor     edx,edx
00000000`77abeebf e85c95ffff      call    ntdll!invalid_parameter (00000000`77ab8420)

ntdll!memcpy_s+0x38:
00000000`77abeec4 b816000000      mov     eax,16h

ntdll!memcpy_s+0x3d:
00000000`77abeec9 488b5c2440      mov     rbx,qword ptr [rsp+40h]
00000000`77abeece 488b742448      mov     rsi,qword ptr [rsp+48h]
00000000`77abeed3 4883c430        add     rsp,30h
00000000`77abeed7 5f              pop     rdi
00000000`77abeed8 c3              ret

ntdll!memcpy_s+0x4d:
00000000`77abeed9 4d85c0          test    r8,r8
00000000`77abeedc 7412            je      ntdll!memcpy_s+0x64 (00000000`77abeef0)

ntdll!memcpy_s+0x52:
00000000`77abeede 483bd3          cmp     rdx,rbx
00000000`77abeee1 720d            jb      ntdll!memcpy_s+0x64 (00000000`77abeef0)

ntdll!memcpy_s+0x57:
00000000`77abeee3 4c8bc3          mov     r8,rbx
00000000`77abeee6 488bd6          mov     rdx,rsi
00000000`77abeee9 e8e2f7fbff      call    ntdll!memcpy (00000000`77a7e6d0)
00000000`77abeeee ebb9            jmp     ntdll!memcpy_s+0x1d (00000000`77abeea9)

ntdll!memcpy_s+0x64:
00000000`77abeef0 4c8bc2          mov     r8,rdx
00000000`77abeef3 33d2            xor     edx,edx
00000000`77abeef5 e8d63ffcff      call    ntdll!memset (00000000`77a82ed0)
00000000`77abeefa 4885f6          test    rsi,rsi
00000000`77abeefd 7505            jne     ntdll!memcpy_s+0x78 (00000000`77abef04)

ntdll!memcpy_s+0x73:
00000000`77abeeff 8d5e16          lea     ebx,[rsi+16h]
00000000`77abef02 eb0a            jmp     ntdll!memcpy_s+0x82 (00000000`77abef0e)

ntdll!memcpy_s+0x78:
00000000`77abef04 483bfb          cmp     rdi,rbx
00000000`77abef07 73bb            jae     ntdll!memcpy_s+0x38 (00000000`77abeec4)

ntdll!memcpy_s+0x7d:
00000000`77abef09 bb22000000      mov     ebx,22h

ntdll!memcpy_s+0x82:
00000000`77abef0e 488364242000    and     qword ptr [rsp+20h],0
00000000`77abef14 4533c9          xor     r9d,r9d
00000000`77abef17 4533c0          xor     r8d,r8d
00000000`77abef1a 33d2            xor     edx,edx
00000000`77abef1c 33c9            xor     ecx,ecx
00000000`77abef1e e8fd94ffff      call    ntdll!invalid_parameter (00000000`77ab8420)
00000000`77abef23 8bc3            mov     eax,ebx
00000000`77abef25 eba2            jmp     ntdll!memcpy_s+0x3d (00000000`77abeec9)

这是 windows 7 64 位系统上 ntdll 模块的一个非常简短的函数 memcpy_s() ,结果并不重要,重要的是过程

1. 确定函数的参数个数

在 64 位 windows 系统上,函数的传递方式相对简单,统一使用寄存器进行传递参数,分别使用:rcxrdxr8 以及 r9 寄存器来传递前 4 个参数,多余的参数依旧使用 stack 来传递。
在这个函数中,我们看到使用到了 r9 寄存器,因此,我们可以判断这个函数共有 4 个参数,下面是 memcpy_s() 函数的原型初形:

memcpy_s(arg1, arg2, arg3, arg4)

分别用 arg1 - arg4 来表示,函数的返回值先暂时放一边,随着分析过程的展开进行填补。

2. 第 4 个参数的处理

下面看看代码:

00000000`77abeea4 4d85c9          test    r9,r9                                   ; arg4
00000000`77abeea7 7504            jne     ntdll!memcpy_s+0x21 (00000000`77abeead)

ntdll!memcpy_s+0x1d:
00000000`77abeea9 33c0            xor     eax,eax                                 ; 返回值
00000000`77abeeab eb1c            jmp     ntdll!memcpy_s+0x3d (00000000`77abeec9)

... ...

ntdll!memcpy_s+0x3d:
00000000`77abeec9 488b5c2440      mov     rbx,qword ptr [rsp+40h]
00000000`77abeece 488b742448      mov     rsi,qword ptr [rsp+48h]
00000000`77abeed3 4883c430        add     rsp,30h
00000000`77abeed7 5f              pop     rdi
00000000`77abeed8 c3              ret

从上面可以看到,这里先判断 arg4 参数,如果为 0 的话,它最终将会函数返回。
于是,我们可以得到下面的逻辑:
if (arg4 == 0)
        return 0;


3. 第 1 个参数的处理

下面看代码:

ntdll!memcpy_s+0x21:
00000000`77abeead 4885c9          test    rcx,rcx
00000000`77abeeb0 7527            jne     ntdll!memcpy_s+0x4d (00000000`77abeed9)

ntdll!memcpy_s+0x26:
00000000`77abeeb2 48214c2420      and     qword ptr [rsp+20h],rcx
00000000`77abeeb7 4533c9          xor     r9d,r9d
00000000`77abeeba 4533c0          xor     r8d,r8d
00000000`77abeebd 33d2            xor     edx,edx
00000000`77abeebf e85c95ffff      call    ntdll!invalid_parameter (00000000`77ab8420)

ntdll!memcpy_s+0x38:
00000000`77abeec4 b816000000      mov     eax,16h

ntdll!memcpy_s+0x3d:
00000000`77abeec9 488b5c2440      mov     rbx,qword ptr [rsp+40h]
00000000`77abeece 488b742448      mov     rsi,qword ptr [rsp+48h]
00000000`77abeed3 4883c430        add     rsp,30h
00000000`77abeed7 5f              pop     rdi
00000000`77abeed8 c3              ret

如果,第 1 个参数 arg1 为 0 的话,它将调用 invalid_parameter() 函数,返回一个代码值(返回状态!)
invalid_parameter() 调用用先将 rdx, r8 以及 r9 寄存清 0,那么这里我姑且认为它也是 4 个参数(注意:这里使用了 edxr8d 和 r9d 寄存器,说明这些参数是 32 位值)并且我们知道 memcpy_s() 函数应该是返回一个状态值!
现在,我们又可以得出它的逻辑(结果起来):

STATUS memcpy_s(arg1, arg2, arg3, arg4)
{
        if (arg4 == 0)
                return 0;

        if (arg1 == 0)
        {
                 invalid_parameters(arg1, 0, 0, 0);
                 return 0x16;                                              // 状态值
         }
}


3. 第 3 个参数的处理

假如,第 1 个参数 arg1 不为 0 的时候呢?

ntdll!memcpy_s+0x21:
00000000`77abeead 4885c9          test    rcx,rcx
00000000`77abeeb0 7527            jne     ntdll!memcpy_s+0x4d (00000000`77abeed9)

... ...

ntdll!memcpy_s+0x4d:
00000000`77abeed9 4d85c0          test    r8,r8                                           ; 第 3 个参数
00000000`77abeedc 7412            je      ntdll!memcpy_s+0x64 (00000000`77abeef0)

... ...

ntdll!memcpy_s+0x64:
00000000`77abeef0 4c8bc2          mov     r8,rdx                                     ; rdx 寄存器的值为 arg2 
00000000`77abeef3 33d2            xor     edx,edx                                    
00000000`77abeef5 e8d63ffcff      call    ntdll!memset (00000000`77a82ed0)
00000000`77abeefa 4885f6          test    rsi,rsi
00000000`77abeefd 7505            jne     ntdll!memcpy_s+0x78 (00000000`77abef04)

它将接下来判断第 3 个参数 arg3,如果 arg3 也为 0 的时候,它将调用 memset()

我们知道 memset() 是置 memory buffer 为某一值的作用,上面所示,它的参数有 3 个,它的逻辑为:
memset(char *dest,  char c, unsigned int count)

在这个函数的调用中,我们可以知道 rdx  寄存器将是传递给 memset() 函数作为第 3 个参数,而 rcx 寄存器正是目标地址值,于是,我们知道 memcpy_s() 函数的第 1 个参数是目标地址值!
于是,我们在这里可以得出:
NT_STATUS memcpy_s([B][COLOR="Red"]char *dest[/COLOR][/B], arg2, arg3, arg4)
{
  if (arg4 == 0) return 0;
  
  if (arg1 == 0) {
    invalid_argeter(arg1, 0, 0, 0)
    return 0x16;
  }

  if (arg3 == 0) {
    memset([COLOR="red"]dest[/COLOR],0, arg2);
    invalid_argeter(arg1, 0, 0, 0)
    return 0x16;            
  }
}

在这一步,我们得出了 memcpy_s() 函数的第 1 个参数,红色标注的。

5. 第 2 个参数与第 4 个参数处理

当第 3 个参数不为 0 的时候,将会继续判断第 2 个和 第 3 个参数:

ntdll!memcpy_s+0x52:
00000000`77abeede 483bd3          cmp     rdx,rbx                                        ; arg2 与 arg4 之间的比较
00000000`77abeee1 720d            jb      ntdll!memcpy_s+0x64 (00000000`77abeef0)

... ...

ntdll!memcpy_s+0x64:
00000000`77abeef0 4c8bc2          mov     r8,rdx
00000000`77abeef3 33d2            xor     edx,edx
00000000`77abeef5 e8d63ffcff      call    ntdll!memset (00000000`77a82ed0)
00000000`77abeefa 4885f6          test    rsi,rsi                                                ; 关键一步, rsi 的值就是 r8 也就是arg1
00000000`77abeefd 7505            jne     ntdll!memcpy_s+0x78 (00000000`77abef04)

... ...

ntdll!memcpy_s+0x78:
00000000`77abef04 483bfb          cmp     rdi,rbx
00000000`77abef07 73bb            jae     ntdll!memcpy_s+0x38 (00000000`77abeec4)

ntdll!memcpy_s+0x7d:
00000000`77abef09 bb22000000      mov     ebx,22h

ntdll!memcpy_s+0x82:
00000000`77abef0e 488364242000    and     qword ptr [rsp+20h],0
00000000`77abef14 4533c9          xor     r9d,r9d
00000000`77abef17 4533c0          xor     r8d,r8d
00000000`77abef1a 33d2            xor     edx,edx
00000000`77abef1c 33c9            xor     ecx,ecx
00000000`77abef1e e8fd94ffff      call    ntdll!invalid_parameter (00000000`77ab8420)
00000000`77abef23 8bc3            mov     eax,ebx
00000000`77abef25 eba2            jmp     ntdll!memcpy_s+0x3d (00000000`77abeec9

这里通过比较 arg2 与 arg4 的大小,当 arg2 小于 arg4 的时候,同样调用 memset(),然后置状态值 0x22,然后返回。
在这一步,我们得到:
NT_STATUS memcpy_s(char *dest, arg2, arg3, arg4)
{
  if (arg4 == 0) return 0;
  
  if (arg1 == 0) {
    invalid_pargeter(dest, 0, 0, 0)
    return 0x16;
  }

  if (arg3 == 0) {
    memset(dest,0, arg2);
    invalid_pargeter(dest, 0, 0, 0)
    return 0x16;            
  }

  if ([B]arg2 < arg4)[/B]
  {
    memset(dest, 0, arg2);
    invalid_pargeter(dest, 0, 0, 0)
    [B]return 0x22[/B];             
  }
}


6. 最后一步,确定 arg2,arg3 以及 arg4

看下面最终的 memcpy() 代码:

ntdll!memcpy_s+0x57:
00000000`77abeee3 4c8bc3          mov     r8,rbx                           ; arg4 是 size
00000000`77abeee6 488bd6          mov     rdx,rsi                          ; r8 是 source
00000000`77abeee9 e8e2f7fbff      call    ntdll!memcpy (00000000`77a7e6d0)
00000000`77abeeee ebb9            jmp     ntdll!memcpy_s+0x1d (00000000`77abeea9)

最终将会调用 memcpy() 进行复制,我们知道 memcpy() 的原型大概是这样的:
memcpy(char *dest,  char *source, unsinged int size)

这里,我们明确的答案了, arg4 将会是 size,arg3 将会是 source

那么,arg2 是什么呢? 通过前面的 if (arg2 < arg4)  的比较,我们可以断定,arg2 是 buffer size,如果 buffer size 小于 count size 值时,那会将会出错。

因此,最后一步,我们得到完全的逻辑:
NT_STATUS memcpy_s(char *dest, arg2, arg3, arg4)
{
  if (arg4 == 0) return 0;
  
  if (arg1 == 0) {
    invalid_argeter(dest, 0, 0, 0)
    return 0x16;
  }

  if (arg3 == 0) {
    memset(dest,0, arg2);
    invalid_argeter(dest, 0, 0, 0)
    return 0x16;            
  }

  if (arg2 < arg4)
  {
    memset(dest, 0, arg2);
    invalid_argeter(dest, 0, 0, 0)
    return 0x22;             
  }
  
  memcpy(dest, arg3, arg4);
  
  return 0;
}


7. 最后,我们整理一下代码,得出最终的一个结果:

下面是还原的结果,这不是原始源代码,只是按照函数的逻辑形成的一个功能和逻辑一样的代码:

STATUS memcpy_s(char *dest, unsigned int buffer_size, char *source, unsigned int count)
{
	STATUS status = STATUS_SUCCESS;

	if (count == 0)	
		return status;

	if (dest == NULL) 
	{
		status = STATUS_INVALID_ADDRESS;
	} 
	else if (source == NULL) 
	{
		memset(dest, 0, buffer_size);
		status = STATUS_INVALID_ADDRESS;
	} 
	else if (buffer_size < count) 
	{
		memset(dest, 0, buffer_size);
		status = STATUS_INVALID_BUFFER_SIZE;
	}
		
	else
		memcpy(dest, source, count);

	
	if (status != STATUS_SUCCESS)
		invalid_parameter(dest, 0, 0, 0);
	
	return status;
}

当然请注意:这里的 status 值是一个表述,在这里不是真实的常量

[课程]Linux pwn 探索篇!

收藏
免费 6
支持
分享
最新回复 (21)
雪    币: 623
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
现在,我们可以知道,memcpy_s() 函数是如何保证它的安全性

当目标 buffer 大小不足放下要复制的数量时,会失败返回失败状态值。避免 override 缓冲区
2011-9-2 01:23
0
雪    币: 278
活跃值: (709)
能力值: ( LV15,RANK:520 )
在线值:
发帖
回帖
粉丝
3
顶,楼主有没兴趣一起玩逆向,交个朋友可以吗?
2011-9-2 09:38
0
雪    币: 623
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
我也只是玩玩而已
交朋友当然可以
2011-9-2 09:57
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
菜鸟一只 也来看看。。。。努力中。。。
2011-9-2 10:11
0
雪    币: 220
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
来支持一下 不错
2011-9-2 10:22
0
雪    币: 343
活跃值: (40)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
7
分析得不错,支持。
2011-9-2 11:00
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
呵呵。楼主很棒,分析的很透彻。。
memcpy_s  这函数只提供汇编源码。。
2011-9-2 17:54
0
雪    币: 278
活跃值: (709)
能力值: ( LV15,RANK:520 )
在线值:
发帖
回帖
粉丝
9
兄弟,你QQ多少
2011-9-2 18:02
0
雪    币: 1149
活跃值: (833)
能力值: ( LV13,RANK:260 )
在线值:
发帖
回帖
粉丝
10
不要加小韬子.....嘿嘿。。。 他是个坏孩子......
2011-9-2 19:50
0
雪    币: 34
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
楼主分析得很到位,不过,如果在分析的时候先把流程图画下,可能效果会更好
2011-9-2 20:42
0
雪    币: 4161
活跃值: (3372)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
分析的很详细
2011-9-3 08:01
0
雪    币: 27
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
正在学习。。。
2011-9-3 14:26
0
雪    币: 113
活跃值: (100)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
晕。这函数本来就是汇编写的。
2011-9-3 14:58
0
雪    币: 27
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
你怎么知道他是坏孩子。O(∩_∩)O~
2011-9-7 14:46
0
雪    币: 36
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
学到不少,感谢.
2011-9-23 09:49
0
雪    币: 2
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
学习。。。
2011-9-23 21:33
0
雪    币: 32
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
辛苦辛苦    好好品味一下咯、、、
2011-10-2 10:59
0
雪    币: 50
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
刚看到  真是个好帖子啊  值得我们学习
2011-10-16 08:12
0
雪    币: 30
活跃值: (39)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
楼主分析很透彻,学习一下,相当不错.
2011-10-16 13:31
0
雪    币: 76
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
2011-10-16 14:08
0
雪    币: 26
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
不错哦,。
2016-5-17 13:16
0
游客
登录 | 注册 方可回帖
返回
//