天天看點

仿真WINDOWS PE加載器的程式

  此程式用來仿真PE加載器,可以直接運作記憶體中的程式。

bool PELoader(char *lpStaticPEBuff, long lStaticPELen)

{

  long lPESignOffset = *(long *)(lpStaticPEBuff + 0x3c);

  IMAGE_NT_HEADERS *pINH = (IMAGE_NT_HEADERS *)(lpStaticPEBuff + lPESignOffset);

  //取加載到記憶體中大小

  long lImageSize = pINH->OptionalHeader.SizeOfImage;

  char *lpDynPEBuff = new char[lImageSize];

  if(lpDynPEBuff == NULL)

  {

    return false;

  }

  memset(lpDynPEBuff, 0, lImageSize);

  //取PE檔案的節數量

  long lSectionNum = pINH->FileHeader.NumberOfSections;

  //計算PE頭資訊及節表資訊占用記憶體大小

  long lPEHeadSize = lPESignOffset + sizeof(IMAGE_NT_HEADERS) + lSectionNum * sizeof(IMAGE_SECTION_HEADER);

  //加載PE頭部資訊及其各個節表

  memcpy(lpDynPEBuff, lpStaticPEBuff, lPEHeadSize);

  //加載各個節

  long lFileAlignMask = pINH->OptionalHeader.FileAlignment - 1;        //各節在磁盤中的對齊掩碼

  long lSectionAlignMask = pINH->OptionalHeader.SectionAlignment - 1;  //各節在load後記憶體中的對齊掩碼

  IMAGE_SECTION_HEADER *pISH = (IMAGE_SECTION_HEADER *)((char *)pINH + sizeof(IMAGE_NT_HEADERS));

  for(int nIndex = 0; nIndex < lSectionNum; nIndex++, pISH++)

  {

    //判定各節的對齊屬性,合法不

    if((pISH->VirtualAddress & lSectionAlignMask) || (pISH->SizeOfRawData & lFileAlignMask))

    {

      //出現非法節

      delete lpDynPEBuff;

      return false;

    }

    //加載改節

    memcpy(lpDynPEBuff + pISH->VirtualAddress, lpStaticPEBuff + pISH->PointerToRawData, pISH->SizeOfRawData);

  }

  //修改導入表,導入程式執行過程中要用到的API函數位址

  if(pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size > 0) //大于0說明有導入表

  {

    IMAGE_IMPORT_DESCRIPTOR *pIID = (IMAGE_IMPORT_DESCRIPTOR *)(lpDynPEBuff + /

      pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

    //循環掃描每個将有函數導入的dll

    for(; pIID->Name != NULL; pIID++)

    {

      IMAGE_THUNK_DATA *pITD = (IMAGE_THUNK_DATA *)(lpDynPEBuff + pIID->FirstThunk);

      HINSTANCE hInstance = LoadLibrary(lpDynPEBuff + pIID->Name);

      if(hInstance == NULL)

      {

        //導入這個dll失敗

        delete lpDynPEBuff;

        return false;

      }

      //循環掃描dll内每個被導入函數

      for(; pITD->u1.Ordinal != 0; pITD++)

      {

        FARPROC fpFun;

        if(pITD->u1.Ordinal & IMAGE_ORDINAL_FLAG32)

        {

          //函數是以序号的方式導入的

          fpFun = GetProcAddress(hInstance, (LPCSTR)(pITD->u1.Ordinal & 0x0000ffff));

        }

        else

        {

          //函數是以名稱方式導入的

          IMAGE_IMPORT_BY_NAME * pIIBN = (IMAGE_IMPORT_BY_NAME *)(lpDynPEBuff + pITD->u1.Ordinal);

          fpFun = GetProcAddress(hInstance, (LPCSTR)pIIBN->Name);

        }

        if(fpFun == NULL)

        {

          //導出這個函數失敗

          delete lpDynPEBuff;

          return false;

        }

        pITD->u1.Ordinal = (long)fpFun;

      }

      FreeLibrary(hInstance);

    }

  }

  //重定位處理

  if(pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size > 0)

  {

    //取第一個重定位塊

    IMAGE_BASE_RELOCATION *pIBR = (IMAGE_BASE_RELOCATION *)(lpDynPEBuff + /

      pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

    long lDifference = (long)lpDynPEBuff - pINH->OptionalHeader.ImageBase;

    //循環每個重定位塊

    for(; pIBR->VirtualAddress != 0; )

    {

      char *lpMemPage = lpDynPEBuff + pIBR->VirtualAddress;

      long lCount = (pIBR->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) >> 1;

      //對這個頁面中的每個需重定位的項進行處理

      short int *pRelocationItem = (short int *)((char *)pIBR + sizeof(IMAGE_BASE_RELOCATION));

      for(int nIndex = 0; nIndex < lCount; nIndex++)

      {

        int nOffset = pRelocationItem[nIndex] &0x0fff;

        int nType = pRelocationItem[nIndex] >> 12;

        //雖然windows定義了很多重定位類型,但是在PE檔案中隻能見到0和3兩種

        if(nType == 3)

        {

          *(long *)(lpDynPEBuff + nOffset) += lDifference;

        }

        else if(nType == 0)

        {

          //什麼也不做

        }

      }

      //pIBR指向下一個重定位塊

      pIBR = (IMAGE_BASE_RELOCATION *)(pRelocationItem + lCount);

    }

  }

  delete lpDynPEBuff;

  return true;

}

繼續閱讀