'PspCidTable'에 해당되는 글 1건

  1. 2008.12.03 [UnExported Symbol]PspCidTable의 주소 구하기.
Programming/Kernel / Driver2008. 12. 3. 11:33
음 이번에는 Export되지 않은 Symbol 중 PspCidTable의 주소를 구하는 법에 대해 알아봅시다.(약간 노가다적인 방법)
저는 두가지 가정을 하고 시작했습니다.
첫째, PspCidTable의 올바른 주소를 알고 있다.
둘째, PspCidTable은 0x80400000(Kernel Base)~0x81000000 까지 존재한다.

제가 시도해 봤던 방법인데 너무 힘들었습니다;

약간 말이 안되는 방법일 수도 있지만

그런대로 Win2000, WinXP, Win2003 다 잘 구해지더군요

주소도 알맞고......

이제부터 그 방법을 알아보도록 하죠.


우선 밑의 스샷을 봅시다.


사용자 삽입 이미지
 
이 스샷은 제가 파일로 출력한 0x80400000~0x82000000까지의 주소에 들어있는 값들입니다.
원래는 0x81000000까지 하려 했지만 확인을 위해서 0x82000000까지 했습니다.
 
여기서, 구해둔 실제 PspCidTable의 주소를 Ctrl-F키를 눌러서 없을 때까지 찾습니다(주소는 적어둡시다).
(저희집 컴퓨터에서의 PspCidTable의 실제주소는 0x80480388 이었습니다)
여기에서, DUMP : 0x80483088(p=0x80454C60), Offset 86808, Valid 17136 이라고 적혀있는 것은
주소에 들어있는 값은 0x80483088이며, 주소는 0x80454C60이며, 덤프 시작지점+86808=0x80454C50이라는 것입니다.
(Valid는 중요치 않습니다)
 
주소에 들어있는 값이 PspCidTable일 때의 주소를 찾았더니 저희집 컴퓨터에서는 다음과 같이 나왔습니다.
0x80483088(p=0x80454C60)
0x80483088(p=0x80454CF0)
0x80483088(p=0x80454D38)
0x80483088(p=0x80454D54)
0x80483088(p=0x80454D9C)
0x80483088(p=0x8046D4C8)
0x80483088(p=0x804E1D88)
0x80483088(p=0x804E3F5C)
(더 있을 수 있음)
 
PspCidTable의 주소값이 들어있는 주소는
0x80454C60, 0x80454CF0, 0x80454D38, 0x80454D54, 0x80454D9C, 0x8046D4C8, 0x804E1D88, 0x804E3F5C이군요.
(그 외에도 더 있을지 모릅니다)
 
이제, 다음 스샷을 봅시다.
사용자 삽입 이미지
 
이제, 0x80454C60, 0x80454CF0, 0x80454D38, 0x80454D54, 0x80454D9C, 0x8046D4C8, 0x804E1D88, 0x804E3F5C의 주소들이
어떤 함수에서 쓰이는지를 알아야 하는데, 방법은 간단합니다.
어떤 *.exe;*.sys;*.dll;*.cpl;*.scr 등의 파일이 무엇을 Export하고 있는지, 시작주소는 어떤지, 등등을 보여주는 기능을 하는 유용한 프로그램이 있습니다.
위 스샷에서 보시는 바와 같이 [Dependency Walker]라는 프로그램이 이 기능을 합니다.
(Microsoft Visual Studio에서 설치시 설치할 수 있습니다)
 
Open을 눌러서 ntoskrnl.exe를 로드하면 위와 비슷하게 나타날 것입니다.
이제 여기서, 0x80454C60, 0x80454CF0, 0x80454D38, 0x80454D54, 0x80454D9C, 0x8046D4C8, 0x804E1D88, 0x804E3F5C가 각각 어떤 함수에 포함되는지 찾아봅시다.
일단, ListView의 헤더를 눌러서 Entry Point순으로  정렬합니다.
그리고, 실제 주소값을 계산합니다.
 
※ 위 스샷에서의 실제 주소값=Entry Point+Base
 
그리고 Base는 밑에서 찾아볼 수 있습니다.
ntoskrnl.exe의 Base는......0x00400000이군요.
하지만, Kernel의 Base는 이와 다릅니다.
ZwQuerySystemInformation()을 이용하거나, LoadLibrary()->GetModuleBase()를 이용해 ntoskrnl.exe의 Base를 구할 수 있지만, 여기서는 대부분의 주어진 값을 사용하겠습니다.
일반적으로, Kernel(ntoskrnl.exe)의 Base Address는 0x80400000입니다.
이 값을 이용하여 위 스샷의 PsGetCurrentProcessId()의 주소를 구하면 0x80400000+0x00054AE4=0x80454AE4 입니다.
 
어떤 주소가 어떤 Export된 함수에 속하기(?) 위해서는
어떤 주소를 pAddr이라 하고, 함수의 주소는 pFuncAddr1, 다음 함수의 주소를 pFuncAddr2라 놓으면
(pFuncAddr2는 pFuncAddr1<pFuncAddr2 를 만족하는 Export된 최소 주소값이다)
pFuncAddr1<pAddr<pFuncAddr2 가 성립하면
pAddr는 pFuncAddr1에 속한다고 가정하고(단, pFuncAddr1과 pFuncAddr2에 UnExport된 Symbol은 없다고 가정)
계산해 보면
0x80454C52<0x80454C60<0x80454CE6이므로 아마도 0x80454C60은 PsLookupProcessThreadByCid()에 속할 것입니다
각각 계산한 결과
PsLookupProcessThreadByCid
PsLookupProcessByProcessId
PsLookupThreadByThreadId
PsCreateSystemThread(이건 좀 의심이 가는군요 다음 Entry Point와의 차이가 좀 커서......)
이 4개의 함수에 PspCidTable이 쓰인다는 결론(?)이 나왔습니다
(물론 이 방법이 100% 맞는다고 확신은 못합니다)
 
이제 각각의 함수들을 뒤지면 실제 PspCidTable의 값이 나오겠군요
PspCidTable 말고도 여러 함수들이 중복해서 쓰일 수 있기에, 범위를 0x80400000~0x81000000까지 한 것입니다
PspCidTable의 주소를 알아내는 함수를 DumpPspCidTable이라 하면
제가 만든 함수는 이렇습니다.
 
PVOID
NTAPI
DumpPspCidTable()
{
 ULONG Offset=0, ValidCount=0, ValidAddresses=0, CurrentAddress=0,
  ValidAddressIndex=0, RealPspCidTableAddressFlag=0;
 ULONG *PspCidTableAddresses=ExAllocatePool(NonPagedPool, 0x400*4);
 ULONG *PspAddressValidFlag=ExAllocatePool(NonPagedPool, 0x400*4);
 BOOLEAN NotEnoughResources=FALSE, PspSetAddressFlag=FALSE;
 PVOID RealPspCidTable=NULL;
 if(!PspCidTableAddresses || !PspAddressValidFlag)
 {
  if(PspAddressValidFlag)
  {
   ExFreePool(PspAddressValidFlag);
  }
  if(PspCidTableAddresses)
  {
   ExFreePool(PspCidTableAddresses);
  }
  return NULL;
 }
 CurrentAddress=(ULONG)PsLookupProcessByProcessId;
 DbgPrint("DumpPspCidTable, Find Address at PsLookupProcessByProcessId()");
 DbgPrint("PsLookupProcessByProcessId(0x%p) : Dump Start", CurrentAddress);
 for(Offset=0;Offset<=0x400;Offset++)
 {
  if(MmIsAddressValid((PULONG)CurrentAddress))
  {
   if(*(PULONG)CurrentAddress>(ULONG)0x80400000L &&
    *(PULONG)CurrentAddress<(ULONG)0x81000000L &&
    MmIsAddressValid((PVOID)(*(PULONG)CurrentAddress)))
   {
    DbgPrint("DUMP : Possible Address %p(p=%p), Offset %X", *(PULONG)CurrentAddress, CurrentAddress, Offset);
    for(ValidAddressIndex=0;ValidAddressIndex<ValidAddresses;ValidAddressIndex++)
    {
     if(ValidAddresses==0) break;
     if(PspCidTableAddresses[ValidAddressIndex]==*(PULONG)CurrentAddress)
     {
      PspAddressValidFlag[ValidAddressIndex]++;
      PspSetAddressFlag=TRUE;
      break;
     }
    }
    if(!PspSetAddressFlag || ValidAddresses==0)
    {
     PspCidTableAddresses[ValidAddresses]=*(PULONG)CurrentAddress;
     PspAddressValidFlag[ValidAddresses]=1;
     ValidAddresses++;
    }
    PspSetAddressFlag=FALSE;
    ValidCount++;
   }
  }
  CurrentAddress++;
 }
 ValidCount=0;
 CurrentAddress=(ULONG)PsLookupProcessThreadByCid;
 DbgPrint("DumpPspCidTable, Find Address at PsLookupProcessThreadByCid()");
 DbgPrint("PsLookupProcessThreadByCid(0x%p) : Dump Start", CurrentAddress);
 for(Offset=0;Offset<=0x400;Offset++)
 {
  if(MmIsAddressValid((PULONG)CurrentAddress))
  {
   if(*(PULONG)CurrentAddress>(ULONG)0x80400000L &&
    *(PULONG)CurrentAddress<(ULONG)0x81000000L &&
    MmIsAddressValid((PVOID)(*(PULONG)CurrentAddress)))
   {
    DbgPrint("DUMP : Possible Address %p(p=%p), Offset %X", *(PULONG)CurrentAddress, CurrentAddress, Offset);
    for(ValidAddressIndex=0;ValidAddressIndex<ValidAddresses;ValidAddressIndex++)
    {
     if(PspCidTableAddresses[ValidAddressIndex]==*(PULONG)CurrentAddress)
     {
      PspAddressValidFlag[ValidAddressIndex]++;
      break;
     }
    }
    ValidCount++;
   }
  }
  CurrentAddress++;
 }
 ValidCount=0;
 CurrentAddress=(ULONG)PsLookupThreadByThreadId;
 DbgPrint("DumpPspCidTable, Find Address at PsLookupThreadByThreadId()");
 DbgPrint("PsLookupThreadByThreadId(0x%p) : Dump Start", CurrentAddress);
 for(Offset=0;Offset<=0x400;Offset++)
 {
  if(MmIsAddressValid((PULONG)CurrentAddress))
  {
   if(*(PULONG)CurrentAddress>(ULONG)0x80400000L &&
    *(PULONG)CurrentAddress<(ULONG)0x81000000L &&
    MmIsAddressValid((PVOID)(*(PULONG)CurrentAddress)))
   {
    DbgPrint("DUMP : Possible Address %p(p=%p), Offset %X", *(PULONG)CurrentAddress, CurrentAddress, Offset);
    for(ValidAddressIndex=0;ValidAddressIndex<ValidAddresses;ValidAddressIndex++)
    {
     if(PspCidTableAddresses[ValidAddressIndex]==*(PULONG)CurrentAddress)
     {
      PspAddressValidFlag[ValidAddressIndex]++;
      break;
     }
    }
    ValidCount++;
   }
  }
  CurrentAddress++;
 }
 ValidCount=0;
 CurrentAddress=(ULONG)PsCreateSystemThread;
 DbgPrint("DumpPspCidTable, Find Address at PsCreateSystemThread()");
 DbgPrint("PsCreateSystemThread(0x%p) : Dump Start", CurrentAddress);
 for(Offset=0;Offset<=0x400;Offset++)
 {
  if(MmIsAddressValid((PULONG)CurrentAddress))
  {
   if(*(PULONG)CurrentAddress>(ULONG)0x80400000L &&
    *(PULONG)CurrentAddress<(ULONG)0x81000000L &&
    MmIsAddressValid((PVOID)(*(PULONG)CurrentAddress)))
   {
    DbgPrint("DUMP : Possible Address %p(p=%p), Offset %X", *(PULONG)CurrentAddress, CurrentAddress, Offset);
    for(ValidAddressIndex=0;ValidAddressIndex<ValidAddresses;ValidAddressIndex++)
    {
     if(PspCidTableAddresses[ValidAddressIndex]==*(PULONG)CurrentAddress)
     {
      PspAddressValidFlag[ValidAddressIndex]++;
      break;
     }
    }
    ValidCount++;
   }
  }
  CurrentAddress++;
 }
 for(ValidAddressIndex=0;ValidAddressIndex<ValidAddresses;ValidAddressIndex++)
 {
  if(PspAddressValidFlag[ValidAddressIndex])
  {
   DbgPrint("Possible Address of PspCidTable : 0x%p, Flag=%d",
    PspCidTableAddresses[ValidAddressIndex],
    PspAddressValidFlag[ValidAddressIndex]);
   if(RealPspCidTableAddressFlag<PspAddressValidFlag[ValidAddressIndex])
   {
    RealPspCidTableAddressFlag=PspAddressValidFlag[ValidAddressIndex];
    RealPspCidTable=(PVOID)PspCidTableAddresses[ValidAddressIndex];
   }
  }
 }
 if(!RealPspCidTable)
 {
  //should never get there
  DbgPrint("Could Not Find PspCidTable!");
  ExFreePool(PspCidTableAddresses);
  ExFreePool(PspAddressValidFlag);
  return NULL;
 }
 DbgPrint("Find PspCidTable at Address %p", RealPspCidTable);
 ExFreePool(PspCidTableAddresses);
 ExFreePool(PspAddressValidFlag);
 return RealPspCidTable;
}
먼저, 정해진 함수로부터의 0x400의 길이만큼 루프를 돌면서 PspCidTable로 추정되는 주소들을 찾습니다.
그리고, 블루스크린과 중복으로 추정되는 값들을 제거해 주기 위해서
추정되는 주소가 유효주소인지를 MmIsAddressValid(*(PULONG)CurrentAddress)로 검사합니다.
유효주소이면 주소들을 저장해둔 배열에 주소에 들어있는 값(PspCidTable로 추정되는 주소값)을 넣어줍니다.
이미 배열에 저장되어 있으면 ValidFlag을 1씩 더해줍니다.
이 방법을 반복합니다.
 
마지막으로, 가장 ValidFlag가 높은 PspCidTable 추정주소값을 찾고, RealPspCidTable에 저장합니다.
이렇게 해서 얻어진 RealPspCidTable이 PspCidTable의 값일 가능성이 높습니다.
 
그리고, 확인 결과 PspCidTable의 주소는 PsCreateSystemThread()를 0x400 까지 탐색한 결과,
Win2000에서는 일치하는 주소가 나왔으나 WinXP에서는 나오지 않았습니다.
그래도 PspCidTable의 올바른 주소는 나왔습니다. -_-;;
 
DbgPrint의 흔적을 기록한 것이 밑 스샷에 있습니다.

사용자 삽입 이미지
 
이와 같이 구해졌습니다.
 
※ 이 방법은 다음 Windows에서 성공적으로 테스트 되었습니다:
Windows 2000 Professional SP4
Windows XP Professional
Posted by skensita