Win2K下關聯程式/埠之程式碼初步分析(轉)

RegisterForBlog發表於2007-09-19
Win2K下關聯程式/埠之程式碼初步分析(轉)[@more@]

  經常見到網友問:如何才能關聯我的程式和埠呀?沒錯,關聯程式和埠是一個非常有用的功能,你可以清楚地知道哪些程式在使用哪些埠,對於查殺木馬很有幫助。可是我們雖然可以使用工作管理員瀏覽程式列表,使用Netstat檢視埠的使用狀況,卻沒有一個命令可以直接關聯程式和埠(WinXP上增加了新的NetStat功能,支援直接檢視埠程式狀況),今年年初的時候,國外出了一個有名的軟體Fport,它可以顯示當前所有的埠及他們所屬的程式,可是,這個軟體並沒有公開原始碼,(太不符合自由軟體精神了吧?),我根據對這個軟體的逆向工程,做了一個類似的工具,在這裡和大家探討一下它的原理。

  一拿到Fport的時候,我就對它進行了API分析,發現除了一些基本的API以外,它還呼叫了NTDLL.dll的幾個未公開API,如NtQuerySystemInfomation,NtQueryInfomationProcess,直覺告訴我,關鍵應該在這兩個函式中,特別是前者。為了能夠理解Fport的執行機理,我們首先要來複習一下SOCKET.SOCKET究竟是什麼?它的中文名稱叫做套介面,但是,實際上我們所謂的SOCKET資料結構只是一個32位的無符號整數(在UNIX中是16位的),它對於Windows作業系統來說其實是一個檔案控制程式碼(SOCKET是檔案?奇怪麼?作業系統在底層實現的時候,常常使用檔案的概念來完成一些基本的功能),這樣的話問題就明朗了,如果我們能夠列舉系統所有的控制程式碼,從中獲得屬性為SOCKET的,不就可以完成Fport的功能?現在你應該想到了,為什麼Fport要呼叫NtQuerySystemInfomation這個API,實際上,NtQuerySystemInfomation這個函式提供了一個簡單的途徑以獲得系統所有的HANDLE,我們先來看看這個函式的原型:

  DWORD NtQuerySystemInformation( DWORD dwRecordType,

  PDWORD pdwHandleList,

  DWORD dwNumBytes,

  PDWORD pdwNumBytesRet );

  我來解釋一下,NtQuerySystemInformation這個函式有四個引數,第一個引數是dwRecordType,這個引數指定了我們所查詢的系統資訊型別,為了查詢系統HANDLE列表,我們定義一個常量#define NT_HANDLE_LIST

  16(這個數值我是查資料得到的,如果誰有更詳細的資料,也請讓我共享一下);第二個引數是一個指標,這個指標用來返回系統控制程式碼列表,在呼叫NtQuerySystemInformation函式之前,必須為這個指標分配足夠的記憶體空間,否則函式呼叫會出錯;第三個引數是指定你為HandleList所分配的記憶體空間大小,單位是byte;第四個引數是NtQuerySystemInformation返回的HandleList的大小;如果NtQuerySystemInformation函式呼叫成功,返回值將是0,否則可以使用GetLastError()獲得詳細的錯誤程式碼。

  一旦NtQuerySystemInformation函式呼叫成功,系統中所有的控制程式碼將被存放在pdwHandleList所指向記憶體空間中,其中,pdwHandleList所指向的第一個32位數,是這個buf所包含的控制程式碼數量,之後是順序排列的控制程式碼指標pHandleInfo,指向的是HANDLEINFO結構:

  typedef struct _HandleInfo

  {

  USHORT dwPid;

  USHORT CreatorBackTraceIndex;

  BYTE

  ObjType;

  BYTE

  HandleAttributes;

  USHORT HndlOffset;

  DWORD

  dwKeObject;

  ULONG

  GrantedAccess;

  }HANDLEINFO, *PHANDLEINFO;

  看到這個結構,我們心中就有底了,控制程式碼資訊中包括了控制程式碼所屬程式的PID,這樣我們就可以關聯程式和SOCKET了,可是,在NT中有各種各樣的控制程式碼:程式控制程式碼、令牌控制程式碼、檔案控制程式碼、視窗控制程式碼……我們怎樣才能判斷一個控制程式碼究竟是不是SOCKET呢?這就要靠HANDLEINFO結構中的ObjType屬性了,經過分析,我們發現,SOCKET控制程式碼的型別值為0x1A,所以,我們將所有型別為0x1A的控制程式碼取出,進行getsockname操作就可以得到當前的程式/埠對應列表,實際上並不然,要知道,我們得到的控制程式碼都屬於其他的程式,在NT中根據程式保護的原則,一個程式沒有辦法直接得到其他程式的各種資訊,特別是控制程式碼,不同程式中的同一控制程式碼(控制程式碼的數值相同)根本就不是同樣的東西,因此,我們還必須進行一次轉換,將其他程式的控制程式碼轉換為本程式的控制程式碼,這個轉換工作只要簡單地呼叫DuplicateHandle函式就可以完成了:

  DuplicateHandle(hSourceProc,

  (HANDLE)pHandleInfo->HndlOffset,

  hCurrentProc,

  &hMyHandle,

  STANDARD_RIGHTS_REQUIRED,

  true,

  0 );

  之後我們就可以透過getsockname、getsockopt等函式來獲得SOCKET的各種屬性了(使我困惑的是,Fport並沒有呼叫getsockname,這說明,應該有更簡單的方法來得到SOCKET控制程式碼的各種屬性,看來我對SOCKET控制程式碼的瞭解程度還是很膚淺呀)

  sockaddr_in name = {0};

  name.sin_family = AF_INET;

  int namelen = sizeof(sockaddr_in);

  SOCKET s = (SOCKET)hMyHandle;

  char szSockType[6][6] = { "NUL", "TCP", "UDP", "RAW", "RDM", "SEQ" };

  iRet = getsockname( s, (sockaddr*)&name, &namelen );

  if ( iRet != SOCKET_ERROR )

  {

  int sockType = 0;

  int optlen = 4;

  iRet = getsockopt(

  s, SOL_SOCKET, SO_TYPE, (char*)&sockType, &optlen );

  printf("PID=%4d PORT=%5d %s ", pHandleInfo->dwPid,

  ntohs( name.sin_port ), szSockType[sockType] );

  }

  至此,程式和埠關聯的工作已經基本完成,可是,還有一些不足的地方,首先,這個軟體不像Fport一樣能夠檢視system程式(就是那個著名的8#程式)的SOCKET,錯誤程式碼是5(access denied),一個簡單的解決方法是將自己做成service,這樣就有了對Local System程式的訪問許可權,不過似乎Fport並不是這麼做的,此為疑點一;其次,由於我對HANDLE屬性的膚淺認識,有的時候會出現誤報或漏報的現象,即使沒有誤報,將所有屬性為0x1A的控制程式碼都進行getsockname效率也略微低了一點,這裡應該有更好的解決方案,此為疑點二;最後,Fport並沒有呼叫socket函式來獲得socket屬性,這說明有一個更簡單直接的方法可以從SOCKET控制程式碼中得到埠、協議等資訊,可惜我不知道,此為疑點三。不過令人欣慰的是,我寫出來的Gport可以在Win2K的非管理員使用者下執行,此時,僅能獲得本使用者所有程式的埠,這大概是Fport所沒有具備的功能。

  寫本文的目的,一方面是為了解答某些網友對Fport原理的疑問,另一方面也是為了拋磚引玉,希望能解答我心中的這些疑點,望各位高手能不吝賜教。

  最後附上Gport的程式碼,Gport的測試版和程式碼檔案可以在我的主頁上下載,地址為:

  附:Gport.cpp

  #include

  #include

  #include

  #include

  #pragma comment ( lib, "ws2_32.lib" )

  // NtQuerySystemInformation record type 16

  #define NT_HANDLE_LIST

  16

  #define OBJECT_TYPE_SOCKET

  0x1A

  #define MAX_HANDLE_LIST_BUF

  0x200000

  // 定義HanleInfo資料結構

  typedef struct _HandleInfo

  {

  USHORT dwPid;

  USHORT CreatorBackTraceIndex;

  BYTE

  ObjType;

  BYTE

  HandleAttributes;

  USHORT HndlOffset;

  DWORD

  dwKeObject;

  ULONG

  GrantedAccess;

  }HANDLEINFO, *PHANDLEINFO;

  // 申明NtQuerySystemInformation()函式

  typedef DWORD (CALLBACK* NTQUERYSYSTEMINformATION)( DWORD, PDWORD, DWORD, PVOID );

  NTQUERYSYSTEMINformATION NtQuerySystemInformation;

  // 判斷SOCKET型別的陣列

  char szSockType[6][6] = { "NUL", "TCP", "UDP", "RAW", "RDM", "SEQ" };

  //

  // RaisePrivleges()函式用來提升本程式的特權

  //

  bool RaisePrivleges( HANDLE hToken, char *pPriv )

  {

  TOKEN_PRIVILEGES tkp;

  if ( !LookupPrivilegevalue( NULL, pPriv, &tkp.Privileges[0].Luid ) )

  {

  printf( "LookupPrivilegevalue Error:%d ", GetLastError() );

  return false;

  }

  tkp.PrivilegeCount = 1;

  tkp.Privileges[0].Attributes |= SE_PRIVILEGE_ENABLED;

  int iRet = AdjustTokenPrivileges(

  hToken,

  false,

  &tkp,

  0,

  (PTOKEN_PRIVILEGES)NULL,

  0 );

  if ( iRet == NULL )

  //AdjustTokenPrivileges函式呼叫失敗

  {

  printf( "AdjustTokenPrivileges Error:%d ", GetLastError() );

  re

  

·上一篇:

·下一篇:
 
     最新更新
·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·

·


| | | | | | |

Copyright © 2004 - 2007 All Rights Reserved

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10763080/viewspace-970134/,如需轉載,請註明出處,否則將追究法律責任。

Win2K下關聯程式/埠之程式碼初步分析(轉)
請登入後發表評論 登入
全部評論

相關文章