如何獲知DLL中函數的參數

來源:互聯網
上載者:User

可以通過反組譯碼來知道介面函數的參數,建議使用W32DSM來分析,也可以直接使用VC來分析,就是麻煩一點。
現在使用W32DSM來具體說明:
1。先開啟需要分析的DLL,然後通過菜單功能-》出口來找到需要分析的函數,雙擊就可以了。
它可以直接定位到該函數。
2。看準該函數的入口,一般函數是以以下代碼作為進入點的。
push ebp
mov ebp, esp
...
3。然後往下找到該函數的出口,一般函數出口有以下語句。
...
ret xxxx;//其中xxxx就是函數差數的所有的位元組數,為4的倍數,xxxx除以4得到的結果
就是參數的個數。
其中參數存放的地方:
ebp+08 //第一個參數
ebp+0C //第二個參數
ebp+10 //第三個參數
ebp+14 //第四個參數
ebp+18 //第五個參數
ebp+1C //第六個參數
。。。。
-------------------------------------------
還有一種經常看到的調用方式:
sub esp,xxxx //開頭部分
//函數的內容
。。。
//函數的內容
add esp,xxxx
ret //結尾部分
其中xxxx/4的結果也是參數的個數。
-------------------------------------------------
還有一種調用方式:
有於該函數比較簡單,沒有參數的壓棧過程,
裡面的
esp+04就是第一個參數
esp+08就是第二個參數
。。。
esp+xx就是第xx/4個參數
你說看到的xx的最大數除以4後的結果,就是該函數所傳遞的參數的個數。
----------------------------------------------
到現在位置,你應該能很清楚的看到了傳遞的參數的個數。至於傳遞的是些什麼內容,還需要進一步的分析。
最方便的辦法就是先找到是什麼軟體在調用此函數,然後通過調試的技術,找到該函數被調用的地方。一般都是PUSH指令
來實現參數的傳遞的。這時可以看一下具體是什麼東西被壓入堆棧了,一般來說,如果參數是整數,一看就可以知道了,
如果是字串的話也是比較簡單的,只要到那個地址上面去看一下就可以了。
如果傳遞的結構的話,沒有很方便的辦法解決,就是讀懂該彙編就可以了。對於以上的分析,本人只其到了拋磚引玉,
希望對大家有點用處。

昨天已經簡單的告訴大家,怎麼知道介面的參數個數了,以及簡單的介面。由於編譯器的最佳化原因,
可能有的參數沒有我前面說的那麼簡單,今天就讓我再來分析一下的DLL的調用的介面。如果在該DLL的
某個函數中,有關於API調用的話,並且調用API的參數整好有一個或多個是該DLL函數的參數的話。
那麼就可以很容易的知道該DLL函數的參數了。
舉例說明:以下彙編代碼通過W32DSM得到。
Exported fn(): myTestFunction - Ord:0001h
:10001010 8B442410 mov eax, dword ptr [esp+10]
:10001014 56 push esi
:10001015 8B74240C mov esi, dword ptr [esp+0C]
:10001019 0FAF742410 imul esi, dword ptr [esp+10]
:1000101E 85C0 test eax, eax
:10001020 7414 je 10001036
:10001022 8B442418 mov eax, dword ptr [esp+18]
:10001026 8B4C2408 mov ecx, dword ptr [esp+08]
:1000102A 6A63 push 00000000
:1000102C 50 push eax
:1000102D 51 push ecx
:1000102E 6A00 push 00000000

* Reference To: USER32.MessageBoxA, Ord:01BEh
|
:10001030 FF15B0400010 Call dword ptr [100040B0]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001020(C)
|
:10001036 8BC6 mov eax, esi
:10001038 5E pop esi
:10001039 C3 ret
-------------------------------------------------------
其中myTestFunction是需要分析的函數,它的裡面調用了USER32.MessageBoxA
這個Function Compute參數個數的時候要注意了,它不是0X18/4的結果,原因是程式入口
的第二條語句又PUSH了一下,PUSH之前的ESP+10就是第4個參數,就是0x10/4 =4
PUSH之後的語句ESP+ XX,
其中(XX-4)/4才對應於第幾個參數。
ESP+0C ==第2個參數
ESP+10 ==第3個參數
ESP+18 ==第5個參數
ESP+08 ==第1個參數
----------------------------這樣共計算出參數的個數是5個,注意PUSH esi之前與PUSH esi之後,
PUSH一下,ESP的值就減了4,特別需要注意的地方!!!然後看函數的返回處RET指令,
由於看到了RET之前給EAX賦了值,所以可以知道該函數就必定返回了一個值,大家都知道EAX的寄存器
是4個位元組的,我們就把它用long來代替好了,現在函數的基本介面已經可以出來了,
long myTestFunction(long p1,long p2,long p3,long p4,long p5);
但是具體的參數類型還需調整,如果該函數裡面沒有用到任何一個參數的話。那麼參數
多少於參數的類型就無所謂了。一般來說這是不太會遇到的。那麼,我們怎麼去得到該函數的
參數呢?請看下面分析:
你有沒有看到* Reference To: USER32.MessageBoxA, Ord:01BEh這一條語句,
這說明了,在它的內部使用了WINAPI::MessageBox函數,我們先看一下它的定義:
int MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
);
它有4個參數。一般我們知道調用API函數的參數是從右往左壓入堆棧的,把它的調用過程
翻譯為偽ASM就是:
PUSH uType
PUSH lpCaption
PUSH lpText
PUSH hWnd
CALL MessageBox
---------------------------------------
我們把這個於上面的語句對應一下,就可以清楚的知道
hWnd = NULL(0)
lpText = ecx
lpCaption = eax
uType = MB_OK(0)
---------------------------------
在往上面看,
原來 EAX 中的值是ESP+18中的內容得到了
ECX 中的值是ESP+08中的內容得到了

那麼到現在為止就可以知道
lpText = ECX = [ESP+08] ==第1個參數
lpCaption = EAX = [ESP+18] ==第5個參數

現在我們可以把該DLL函數介面進一步寫成:
long myTestFunction(LPCTSTR lpText,long p2,long p3,long p4,LPCTSTR lpCaption);

至於第3個參數ESP+10,然後找到該參數使用的地方,imul esi, dword ptr [esp+10]有這麼一條指令。
因為imul是乘法指令,我們可以肯定是把ESP+10假設位long是不會錯的,同理可以知道第2個參數esp+0C
肯定用long也不會錯了,至於第4個參數,它只起到了一個測試的作用,
mov eax, dword ptr [esp+10]
test eax, eax
je 10001036
看到這個參數的用法了嗎?
把它翻譯位C語言就是:
if(p3)
{
//做je 10001036下面的那些指令
}
return ;
到現在為止可以把第3個參數看成是個指標了吧!就是如果p3為空白就直接返回,如果不空就做其它一下事情。

好了,到現在位置可以把正確的介面給列出來了:
long myTestFunction(LPCTSTR lpText,long n1,char *pIsNull,long n2,LPCTSTR lpCaption);
哈哈,現在成功了!!!

long CryptExtOpenCER(long p1,long p2,LPCSTR p3,long p4);
其中第3個參數可能是檔案名稱,
或者是PCERT_BLOB
它有CERT_QUERY_OBJECT_FILE,或者是CERT_QUERY_OBJECT_BLOB來決定。
---------------------------------------------------------------
今天想到了一個很好的辦法,來解決參數的問題,不過有一定難度。
1。根據以前講的各種方法,可以很快速的知道參數的個數,假設該函數
名稱為MyTestFunc,參數的個數為3個。
於是可以定義如下:
long MyTestFunc(long p1,long p2,long p3);
2。安裝一個HOOK(DLL)
3。通過別的程式調用,觸發HOOK,調試到HOOK裡面,就可以很清楚的知道
調用的參數,數值。
-------------
此方法本人還沒有去實現,相信肯定是可以的。這樣得到的參數應該相當準確。 

 

 

 

首先需要知道該函數有幾個參數,然後再細化參數類型。詳細分析過程如下:
    可以通過反組譯碼來知道介面函數的參數,建議使用W32DSM來分析,也可以直接使用VC來分析,就是麻煩一點。
現在使用W32DSM來具體說明:
1。先開啟需要分析的DLL,然後通過菜單功能-》出口來找到需要分析的函數,雙擊就可以了。它可以直接定位到該函數。
2。看準該函數的入口,一般函數是以以下代碼作為進入點的。
push ebp
mov  ebp, esp
...
3。然後往下找到該函數的出口,一般函數出口有以下語句。
...
ret  xxxx;//其中xxxx就是函數差數的所有的位元組數,為4的倍數,xxxx除以4得到的結果
就是參數的個數。
其中參數存放的地方:
ebp+08     //第一個參數
ebp+0C     //第二個參數
ebp+10     //第三個參數
ebp+14     //第四個參數
ebp+18     //第五個參數
ebp+1C     //第六個參數
。。。。
-------------------------------------------
還有一種經常看到的調用方式:
sub esp,xxxx     //開頭部分
//函數的內容
。。。
//函數的內容
add esp,xxxx
ret              //結尾部分
其中xxxx/4的結果也是參數的個數。 
-------------------------------------------------
還有一種調用方式:
有於該函數比較簡單,沒有參數的壓棧過程,
裡面的
esp+04就是第一個參數
esp+08就是第二個參數
。。。
esp+xx就是第xx/4個參數
你說看到的xx的最大數除以4後的結果,就是該函數所傳遞的參數的個數。
----------------------------------------------
到現在位置,你應該能很清楚的看到了傳遞的參數的個數。至於傳遞的是些什麼內容,還需要進一步的分析。
最方便的辦法就是先找到是什麼軟體在調用此函數,然後通過調試的技術,找到該函數被調用的地方。一般都是PUSH指令
來實現參數的傳遞的。這時可以看一下具體是什麼東西被壓入堆棧了,一般來說,如果參數是整數,一看就可以知道了,
如果是字串的話也是比較簡單的,只要到那個地址上面去看一下就可以了。
如果傳遞的結構的話,沒有很方便的辦法解決,就是讀懂該彙編就可以了。

 

 

    另外由於編譯器的最佳化原因,可能有的參數沒有我前面說的那麼簡單。如果在該DLL的某個函數中,有關於API調用的話,
並且調用API的參數整好有一個或多個是該DLL函數的參數的話。那麼就可以很容易的知道該DLL函數的參數了。
舉例說明:以下彙編代碼通過W32DSM得到。
Exported fn(): myTestFunction - Ord:0001h
:10001010 8B442410                mov eax, dword ptr [esp+10]
:10001014 56                      push esi
:10001015 8B74240C                mov esi, dword ptr [esp+0C]
:10001019 0FAF742410              imul esi, dword ptr [esp+10]
:1000101E 85C0                    test eax, eax
:10001020 7414                    je 10001036
:10001022 8B442418                mov eax, dword ptr [esp+18]
:10001026 8B4C2408                mov ecx, dword ptr [esp+08]
:1000102A 6A63                    push 00000000
:1000102C 50                      push eax
:1000102D 51                      push ecx
:1000102E 6A00                    push 00000000

* Reference To: USER32.MessageBoxA, Ord:01BEh
                                  |
:10001030 FF15B0400010            Call dword ptr [100040B0]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:10001020(C)
|
:10001036 8BC6                    mov eax, esi
:10001038 5E                      pop esi
:10001039 C3                      ret
-------------------------------------------------------
其中myTestFunction是需要分析的函數,它的裡面調用了USER32.MessageBoxA
這個Function Compute參數個數的時候要注意了,它不是0X18/4的結果,原因是程式入口
的第二條語句又PUSH了一下,PUSH之前的ESP+10就是第4個參數,就是0x10/4 =4
PUSH之後的語句ESP+ XX,
其中(XX-4)/4才對應於第幾個參數。
ESP+0C ==第2個參數
ESP+10 ==第3個參數
ESP+18 ==第5個參數
ESP+08 ==第1個參數
----------------------------這樣共計算出參數的個數是5個,注意PUSH esi之前與PUSH esi之後,
PUSH一下,ESP的值就減了4,特別需要注意的地方!!!然後看函數的返回處RET指令,
由於看到了RET之前給EAX賦了值,所以可以知道該函數就必定返回了一個值,大家都知道EAX的寄存器
是4個位元組的,我們就把它用long來代替好了,現在函數的基本介面已經可以出來了,
long myTestFunction(long p1,long p2,long p3,long p4,long p5);
但是具體的參數類型還需調整,如果該函數裡面沒有用到任何一個參數的話。那麼參數
多少於參數的類型就無所謂了。一般來說這是不太會遇到的。那麼,我們怎麼去得到該函數的
參數呢?請看下面分析:
      你有沒有看到* Reference To: USER32.MessageBoxA, Ord:01BEh這一條語句,
這說明了,在它的內部使用了WINAPI::MessageBox函數,我們先看一下它的定義:
int MessageBox(
  HWND hWnd,          // handle of owner window
  LPCTSTR lpText,     // address of text in message box
  LPCTSTR lpCaption,  // address of title of message box
  UINT uType          // style of message box
);
它有4個參數。一般我們知道調用API函數的參數是從右往左壓入堆棧的,把它的調用過程
翻譯為偽ASM就是:
              PUSH uType
              PUSH lpCaption
              PUSH lpText
              PUSH hWnd
              CALL MessageBox
---------------------------------------
我們把這個於上面的語句對應一下,就可以清楚的知道
hWnd       = NULL(0)
lpText     = ecx
lpCaption  = eax
uType      = MB_OK(0)
---------------------------------
在往上面看,
原來 EAX 中的值是ESP+18中的內容得到了
     ECX 中的值是ESP+08中的內容得到了

那麼到現在為止就可以知道
lpText    = ECX = [ESP+08]    ==第1個參數
lpCaption = EAX = [ESP+18]    ==第5個參數

現在我們可以把該DLL函數介面進一步寫成:
long myTestFunction(LPCTSTR lpText,long p2,long p3,long p4,LPCTSTR lpCaption);

至於第3個參數ESP+10,然後找到該參數使用的地方,imul esi, dword ptr [esp+10]有這麼一條指令。
因為imul是乘法指令,我們可以肯定是把ESP+10假設位long是不會錯的,同理可以知道第2個參數esp+0C
肯定用long也不會錯了,至於第4個參數,它只起到了一個測試的作用,
mov eax, dword ptr [esp+10]
test eax, eax
je 10001036
看到這個參數的用法了嗎?
把它翻譯位C語言就是:
if(p3)
{
    //做je 10001036下面的那些指令
}
return ;
到現在為止可以把第3個參數看成是個指標了吧!就是如果p3為空白就直接返回,如果不空就做其它一下事情。

好了,到現在位置可以把正確的介面給列出來了:
long myTestFunction(LPCTSTR lpText,long n1,char *pIsNull,long n2,LPCTSTR lpCaption);

//---------------以上內容從http://www.csdn.net/expert/topic/636/636873.xml整理而來--------------------

 

下面使用APIHOOK2.0來分析該參數,相當方便。為了你能更好的理解下面的程式,現舉一個MessageBox的例子,
假設本人不知道該函數的參數個數,及參數類型。
    首相擷取APIHOOK2.0(網上有的,自己找一下就可以了),解開口,把系統對應的ApiHooks.exe,ApiHooks.dll
放入系統目錄中,或者在PATH中能找到的地方。把ApiHooks.lib,ApiHooks.h放入你的工程中,需要自己建立一個,如:MyApiHook,
類型WIN32 Dynamic-Link Library,(空的),然後添加MyApiHook.cpp,該檔案中的內容如下:
//--------------------------------MyApiHook.cpp檔案開始-----------------------------------------
// MyApiHook.cpp : Defines the entry point for the DLL application.

//功能:把MyApiHook.dll注入到testdlg.exe的進程中,替換testdlg.exe中所調用的DLL中API
//具體使用如下:
//c:/>apihooks -nq MyApiHook.dll testdlg.exe
//apihooks = apihook.exe 和 apihooks.dll
// -nq    新開啟一個程式(testdlg.exe),q不彈出資訊
//MyApiHook.dll = By This File Create
//testdlg.exe = 需要替換的程式

#include <stdio.h>

#define  WIN32_LEAN_AND_MEAN  
#include <windows.h>
#include "ApiHooks.h"

int  (WINAPI *pFunction)(long  p1,long  p2,long  p3, long   p4);
typedef int  (WINAPI Function)(long   p1,long p2,long p3, long p4);

int WINAPI MyMessageBoxA(long  p1,long  p2,long  p3, long p4)
{
 const nCountParam = 4;
 long pp[nCountParam];
 pp[0] = p1,pp[1] = p2,pp[2] = p3,pp[3] = p4;

 FILE *fp = fopen("c://1.txt","w");
        char szBuf[1024];

 sprintf(szBuf, "參數內容的列表,很容易判斷是否是字串,或者為NULL/n");
 fputs(szBuf, fp);

 for(long i = 0; i < nCountParam; i++)
 {
  sprintf(szBuf,"[p%d] = 0x00000000(0)/n",i);
  _try {
   if(pp[i])
    sprintf(szBuf,"[p%d] = 0x%08x(%d)  /t/"%s/"/n",i,pp[i],pp[i],pp[i]);
  }_except(1,1)
  {
   sprintf(szBuf,"[p%d] = 0x%08x(%d)/n",i,pp[i],pp[i]);
  }
  fputs(szBuf,fp);
 }
 fclose(fp);

    return(pFunction(p1, p2, p3,p4));
}

extern "C"__declspec(dllexport) API_HOOK ApiHookChain[2] = {
 {"USER32.DLL","MessageBoxA",HOOK_EXACT, NULL, NULL, MyMessageBoxA},
 {HOOKS_END}
};

BOOL APIENTRY DllMain( HANDLE hModule,DWORD  ul_reason_for_call, LPVOID lpReserved)
{
 HMODULE  hDLL = LoadLibrary("USER32.DLL");
 if(hDLL)
 {
  pFunction = (Function *)GetProcAddress(hDLL,"MessageBoxA");
  FreeLibrary(hDLL);
 }
    return TRUE;
}
//--------------------------------MyApiHook.cpp檔案結束-----------------------------------------
上面的這個例子分析了大家都熟悉的MessageBox,假設你不知道該函數的參數,通過上面講的方法,可以很容易的知道該
函數共有4個參數,有返回數。
於是我們可以把該函數定義為:
int WINAPI MessageBox(long   p1,long p2,long p3, long p4);
然後自己定義一個MyMessageBoxA的函數(此函數可以很方便的判斷該參數是否為字串)
     int WINAPI MyMessageBoxA(long  p1,long  p2,long  p3, long p4)
通過該函數可以產生的該函數的調用的實際內容,

//------------以下為筆者機器上所得到的資訊-----------
參數內容的列表,很容易判斷是否是字串,或者為NULL
[p0] = 0x00000000(0)
[p1] = 0x00416698(4286104)   "測試對話方塊"
[p2] = 0x004166a0(4286112)   "資訊"
[p3] = 0x00000040(64)
//-------------------------------
根據以上資訊,可以很容易的知道第2,3個參數為字串。然後根據裡面的內容可以很容易的知道該參數的實際用途。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.