作 者: 天高
時 間: 2011-09-02,01:15:29鏈接: http://bbs.pediy.com/showthread.php?t=139643
看過論壇的一些關於逆向的貼子
說說我的看法:逆向工程並不是對代碼進行分析就算了,實際上要對工程進行一定的還原(源代碼形式),當然難度挺大,要對算法非常熟悉。要係統的數據結構非常熟悉,才有能力推還原工程的數據結構。
這裡僅對一個簡短的函數進行還原,當然並不可能還原為原作者源代碼,是還原為在函數的邏輯思想上最接近的形式。
代碼:
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 系統上,函數的傳遞方式相對簡單,統一使用寄存器進行傳遞參數,分別使用:rcx, rdx, r8 以及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 個參數(注意:這裡使用了edx, r8d 和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(char *dest, arg2, arg3, arg4)
{
if (arg4 == 0) return 0;
if (arg1 == 0) {
invalid_argeter(arg1, 0, 0, 0)
return 0x16;
}
if (arg3 == 0) {
memset(dest,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 (arg2 < arg4)
{
memset(dest, 0, arg2);
invalid_pargeter(dest, 0, 0, 0)
return 0x22;
}
}
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 值是一個表述,在這裡不是真實的常量
没有评论:
发表评论