可以参考用来写自己的猛壳

可以参考用来写自己的猛壳

Ntoskrnl

This article is just for fun, don't take it too serious. Some time ago a friend (Littleluk) asked me if i was interested in helping him with the coding stuff related the unpacking of themida (the commercial successor of xprotector). I agreed. However, i had second thoughts about it. 'Cause, to be sincere, i don't like to waste my time over useless things and themida sure is one of them. For those who still don't know what i'm talking about : themida is a packer, moreover it's a packer who runs partly in kernel-mode. This means: if you try to debug themida with a debugger, it will crash your system. Very unprofessional, i know. And that's mainly the reason i look at themida as a toy for reversers. Lets get this straight, themida will never be used by major companies, nobody would protect a good software with a protector who patches the kernel of windows, patches the SDT, patches the IDT, in certain situations makes you reboot your computer etc etc? This means themida isn't widely used and there's no real interest in unpacking it other than the fun itself. I have no fun doing useless things, but since Littleluk is the reverser, i have just to code some stuff (and using old code snippets i've already coded). And this is what this article is all about, a tool i wrote. It's not a tool to unpack themida (that would be impossible: Littleluk hasn't reversed it yet). The tool makes work some tools you already have on you computer against themida.

A few warnings:

1 - this article is nothing hardcore, don't let you impress by the driver coding stuff, it's ridiculous stuff for a driver writer (and i say that 'cause sometime in the reversing scene it's easy to find people with no knowledge of kernel-mode programming).

2 - the things you're going to read in this article will be obsolete in a few years (as the tricks themida uses).

3 - i never touched themida, i wrote the tool out of my experience and the results i got from Littleluk.

4 - i'm not going to explain what SDT, IDT and other internals relative concepts mean. If you ignore the meaning look on the web. I have no time to explain more than necessary, in fact this is one of the reason i'm releasing this article right now. I just have available a coule of days and then i have to go back to work (real work).

5 - i'm going to explain what i know about themida, or better what you need to know to understand the tool i wrote. I'm not reversing themida, so i don't care about a lot things.

6 - i don't know if there will be another version of the AntiMida, it depends on the information which is given to me by the reverser/s and on how much free time i'll have.

7 - it doesn't work if u're using PAE extension (i was too lazy to add some code).

8 - The way the tool acts wasn't absolutely necessary, there were other ways.

The victim which was used for the tests is nothing else than the themida itself (the demo version i mean). You can download it from the official webpage (current version is 1.0.0.5).

The AntiMida is not a planned tool, everytime there was a problem i tried to code the solution for it. At the moment AntiMida lets you:

1 - use common user-mode applications to dump themida.
2 - use tools like imprec, winhex (to see the proc memory), etc.
3 - monitor file and registry access.

But one thing at a time. The first step was to dump themida. How? First we had to know what themida does to protect itself against dumping. Actually the first idea to dump themida was to use KeAttachProcess, we dumped ntoskrnl (with wark) and saw that the keattachprocess was patched with a jmp to a themida routine. So, to use keattachprocess it was necessary to pacth the ntoskrnl first (i paste later the code). Here's the routine i wrote with KeAttachProcess:

    case CODE_READ_MEM:
      {
         MemReadInfo mri;
         ULONG_PTR ptr, addr;
         BYTE *Buffer;
         UINT x, y;

         RtlCopyMemory(&mri, pInput, sizeof (MemReadInfo));

         if (PsLookupProcessByProcessId(mri.PID, &ptr) != STATUS_SUCCESS)
            return STATUS_INVALID_PARAMETER;

         Buffer = (BYTE *)  ExAllocatePool(NonPagedPool, dwOutputSize);

         if (Buffer == NULL)
            return STATUS_INVALID_PARAMETER;

         KeAttachProcess(ptr);

         if (dwOutputSize <= 0x1000)
         {
            x = 1;
         }
         else
         {
            x = dwOutputSize / 0x1000;

            if (dwOutputSize % 0x1000 != 0)
               x++;
         }

         for (y = 0; y < x; y++)
         {
            addr = y * 0x1000 + (ULONG_PTR) mri.Address;

            if (MmIsAddressValid((PVOID) addr) == FALSE)
            {
               ExFreePool(Buffer);
               return STATUS_INVALID_PARAMETER;
            }
         }

         RtlCopyMemory(Buffer, mri.Address, dwOutputSize);

         KeDetachProcess();

         RtlCopyMemory(pOutput, Buffer, dwOutputSize);

         ExFreePool(Buffer);

         *pdwInfo = dwOutputSize;

         break;
      }

I pasted it just for information, 'cause this code isn't used anymore by the AntiMida. It was obvious that themida was hooking the SDT, so after a scanning with sdtrestore a list of hooked services was given to me:

ZwAllocateVirtualMemory 11 --[hooked by unknown at F5938BC4]--
ZwCreateThread 35 --[hooked by unknown at F5938CBE]--
ZwDebugContinue 3A --[hooked by unknown at F59391A0]--
ZwQueryVirtualMemory B2 --[hooked by unknown at F5938ACA]--
ZwReadVirtualMemory BA --[hooked by unknown at F5938014]--
ZwTerminateProcess 101 --[hooked by unknown at F59389D0]--
ZwWriteVirtualMemory 115 --[hooked by unknown at F5938000]--

Ok ZwAllocateVirtualMemory, ZwQueryVirtualMemory, ZwReadVirtualMemory, ZwWriteVirtualMemory, ZwCreateThread make sense, since we could use all these functions to dump themida. ZwDebugContinue is to avoid debugging, although themida fucks with the IDT as well (so this is way not the only trick against debugging, would be too easy, of course). ZwTerminateProcess is unimportant, i guess it's only hooked to know when a protected process is killed. Of course if you try to restore one of these services themida crashes the system.

The idea i had was to build a tool which would make other already existing tools work. So i organized an interface with a list of the running processes and the possibility of selecting one of those processes and make it AntiMida, this means immune against themida. A brief look at the interface is thousand of words worth.

[ Last edited by progholic on 2005-12-15 at 19:32 ]

TOP

When the the little wizard is pressed, a dll is injected in the address space of the selected process, the functions themida is protecting itself against are hooked and redirected to the injected dll, each time a hooked function is called, the dll calls a driver which process the operation. Ok, but how does my driver process the requested operation? You'll see that later. Now lets take a look on the injection/hooking routines:
BOOL InjectModule(IN ULONG_PTR ProcessID, IN TCHAR *ModuleName,
              OUT ULONG_PTR *BaseAddress OPTIONAL)
{
   HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE,
      ProcessID);

   if (hProcess == NULL)
      return FALSE;

   HANDLE hFile = CreateFile(ModuleName, GENERIC_READ, FILE_SHARE_READ,
      NULL, OPEN_EXISTING, 0, NULL);

   if (hFile == INVALID_HANDLE_VALUE)
   {
      CloseHandle(hProcess);
      return FALSE;
   }

   UINT PE_Size = GetFileSize(hFile, NULL);
   BYTE *PE_Buffer = (BYTE *) VirtualAlloc(NULL, PE_Size,
      MEM_COMMIT, PAGE_READWRITE);

   if (PE_Buffer == NULL)
   {
      CloseHandle(hProcess);
      CloseHandle(hFile);
      return FALSE;
   }

   DWORD BR;

   if (!ReadFile(hFile, PE_Buffer, PE_Size, &BR, NULL))
   {
      VirtualFree(PE_Buffer, 0, MEM_RELEASE);
      CloseHandle(hProcess);
      CloseHandle(hFile);
      return FALSE;
   }

   CloseHandle(hFile);

   BYTE *PE_Image;

   IMAGE_DOS_HEADER *ImgDosHdr;
   IMAGE_NT_HEADERS *ImgNtHdrs;

   ULONG_PTR ImgBase, Delta, Reloc_Offset;
   IMAGE_BASE_RELOCATION *ImgBaseReloc;
   WORD *wData;
   UINT i, nItems;
   ULONG_PTR Offset;
   DWORD Type;
   ULONG_PTR *Block, BlockOffs;

   ULONG_PTR IT_Offset;
   IMAGE_IMPORT_DESCRIPTOR *ImgImpDescr;
   UINT x = 0, y = 0;
   CHAR *DllName;
   ULONG_PTR *Thunks, *FThunks;
   IMAGE_IMPORT_BY_NAME *ImgImpName;

   _try
   {
      ImgDosHdr = (IMAGE_DOS_HEADER *) PE_Buffer;

      ImgNtHdrs = (IMAGE_NT_HEADERS *) (ImgDosHdr->e_lfanew +
         (ULONG_PTR) PE_Buffer);

      if (ImgDosHdr->e_magic != IMAGE_DOS_SIGNATURE ||
         ImgNtHdrs->Signature != IMAGE_NT_SIGNATURE)
      {
         VirtualFree(PE_Buffer, 0, MEM_RELEASE);
         CloseHandle(hProcess);
         return FALSE;
      }

      ImgBase = (ULONG_PTR) VirtualAllocEx(hProcess,
         (PVOID) 0, //ImgNtHdrs->OptionalHeader.ImageBase,
         ImgNtHdrs->OptionalHeader.SizeOfImage, MEM_COMMIT,
         PAGE_EXECUTE_READWRITE);

      if (ImgBase == NULL)
      {
         VirtualFree(PE_Buffer, 0, MEM_RELEASE);
         CloseHandle(hProcess);
         return FALSE;
      }

      //
      // is file image base == memory image base?
      // if not, relocate
      //

      if (ImgNtHdrs->OptionalHeader.ImageBase != ImgBase)
      {
         Delta = ImgBase - ImgNtHdrs->OptionalHeader.ImageBase;

         if (!ImgNtHdrs->OptionalHeader.DataDirectory
            [IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress ||
            !(Reloc_Offset = RvaToOffset(ImgNtHdrs,
            ImgNtHdrs->OptionalHeader.DataDirectory
            [IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress)))
         {
            VirtualFreeEx(hProcess, (LPVOID) ImgBase, 0, MEM_RELEASE);
            VirtualFree(PE_Buffer, 0, MEM_RELEASE);
            CloseHandle(hProcess);
            return FALSE;
         }

         ImgBaseReloc = (IMAGE_BASE_RELOCATION *)
            (Reloc_Offset + (ULONG_PTR) PE_Buffer);

         do
         {
            if (!ImgBaseReloc->SizeOfBlock)
               break;

            nItems = (ImgBaseReloc->SizeOfBlock -
               IMAGE_SIZEOF_BASE_RELOCATION) / sizeof (WORD);

            wData = (WORD *)(IMAGE_SIZEOF_BASE_RELOCATION +
               (ULONG_PTR) ImgBaseReloc);

            for (i = 0; i < nItems; i++)
            {
               Offset = (*wData & 0xFFF) + ImgBaseReloc->VirtualAddress;

               Type = *wData >> 12;

               if (Type != IMAGE_REL_BASED_ABSOLUTE)
               {
                  BlockOffs = RvaToOffset(ImgNtHdrs, Offset);

                  if (BlockOffs == NULL)
                  {
                     VirtualFreeEx(hProcess, (LPVOID) ImgBase, 0, MEM_RELEASE);
                     VirtualFree(PE_Buffer, 0, MEM_RELEASE);
                     CloseHandle(hProcess);
                     return FALSE;
                  }

                  Block = (DWORD *)(BlockOffs + (ULONG_PTR) PE_Buffer);

                  *Block += Delta;
               }

               wData++;
            }

            ImgBaseReloc = ( PIMAGE_BASE_RELOCATION) wData;

         } while (*(DWORD *) wData);
      }

      //
      // fill the import addres table
      //

      if (ImgNtHdrs->OptionalHeader.DataDirectory
         [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
      {
         IT_Offset = RvaToOffset(ImgNtHdrs,
            ImgNtHdrs->OptionalHeader.DataDirectory
            [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

         ImgImpDescr = (IMAGE_IMPORT_DESCRIPTOR *) (IT_Offset +
            (ULONG_PTR) PE_Buffer);


         // for each descriptor
         while (ImgImpDescr[x].FirstThunk != 0)
         {
            DllName = (CHAR *) (RvaToOffset(ImgNtHdrs,
               ImgImpDescr[x].Name) + (ULONG_PTR) PE_Buffer);

            Thunks = (ULONG_PTR *) (RvaToOffset(ImgNtHdrs,
               ImgImpDescr[x].OriginalFirstThunk != 0 ?
               ImgImpDescr[x].OriginalFirstThunk :
            ImgImpDescr[x].FirstThunk) + (ULONG_PTR) PE_Buffer);

            FThunks = (ULONG_PTR *) (RvaToOffset(ImgNtHdrs,
               ImgImpDescr[x].FirstThunk) + (ULONG_PTR) PE_Buffer);

            y = 0;

            //
            // every imported function of the module
            //

            while (Thunks[y] != 0)
            {
               //
               // imported by ordinal?
               //

               if (Thunks[y] & IMAGE_ORDINAL_FLAG)
               {
                  FThunks[y] = (ULONG_PTR) GetProcAddress(
                     GetModuleHandle(DllName),
                     (LPCSTR) (Thunks[y] - IMAGE_ORDINAL_FLAG));

                  y++;

                  continue;
               }

               ImgImpName = (IMAGE_IMPORT_BY_NAME *) (RvaToOffset(
                  ImgNtHdrs, Thunks[y]) + (ULONG_PTR) PE_Buffer);

               FThunks[y] = (ULONG_PTR) GetProcAddress(GetModuleHandle(DllName),
                  (LPCSTR) &ImgImpName->Name);

               y++;
            }

            x++;
         }


      }
   }
   _except (EXCEPTION_EXECUTE_HANDLER)
   {
      VirtualFreeEx(hProcess, (LPVOID) ImgBase, 0, MEM_RELEASE);
      VirtualFree((LPVOID) PE_Buffer, 0, MEM_RELEASE);
      CloseHandle(hProcess);
      return FALSE;
   }

   //
   // create virtual image of the PE file
   //

   PE_Image = (BYTE *) VirtualAlloc(NULL,
      ImgNtHdrs->OptionalHeader.SizeOfImage,
      MEM_COMMIT, PAGE_READWRITE);

   if (PE_Image == NULL)
   {
      VirtualFreeEx(hProcess, (LPVOID) ImgBase, 0, MEM_RELEASE);
      VirtualFree(PE_Buffer, 0, MEM_RELEASE);
      CloseHandle(hProcess);
      return FALSE;
   }

   ZeroMemory(PE_Image, ImgNtHdrs->OptionalHeader.SizeOfImage);

   //
   // copy headers
   //

   IMAGE_SECTION_HEADER *Sect;

   Sect = IMAGE_FIRST_SECTION(ImgNtHdrs);

   RtlCopyMemory(PE_Image, PE_Buffer, Sect[0].PointerToRawData);

   //
   // map sections
   //

   for (UINT j = 0; j < ImgNtHdrs->FileHeader.NumberOfSections; j++)
   {
      BYTE *Source = (BYTE *)(Sect[j].PointerToRawData +
         (ULONG_PTR) PE_Buffer);

      BYTE *Dest = (BYTE *)(Sect[j].VirtualAddress +
         (ULONG_PTR) PE_Image);

      RtlCopyMemory(Dest, Source, Sect[j].SizeOfRawData);
   }

   BOOL bRet = WriteProcessMemory(hProcess, (LPVOID) ImgBase,
      PE_Image, ImgNtHdrs->OptionalHeader.SizeOfImage, &BR);

   if (!bRet)
      VirtualFreeEx(hProcess, (LPVOID) ImgBase, 0, MEM_RELEASE);

   VirtualFree(PE_Image, 0, MEM_RELEASE);
   VirtualFree(PE_Buffer, 0, MEM_RELEASE);
   CloseHandle(hProcess);

   if (bRet == TRUE && BaseAddress != NULL)
      *BaseAddress = ImgBase;

   return bRet;
}

To fill the import table i used no remote getprocaddress since the dll imports just not relocated system dlls, so a locale getprocaddress is enough. Here's the hooking routine:

void CAntiMidaDlg::OnBnClickedAntimida()
{
   ProcList.GetItemText(ProcList.GetNextItem(-1, LVNI_SELECTED), 1,
      Buffer, sizeof (Buffer) -1);

   ULONG_PTR PID = _tcstoul(Buffer, 0, 16);
   
   wsprintf(Buffer, _T("%samdll.dll"), CurDir);

   ULONG_PTR BaseAddress;

   //
   // inject the module
   //

   if (InjectModule(PID, _T("amdll.dll"), &BaseAddress))
   {
      HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);

      if (hProcess == NULL)
      {
         MessageBox(_T("Cannot make the process AntiMida"), "AntiMida");
         return;
      }

      //
      // hook ReadProcessMemory
      //

      ULONG_PTR Addr = (ULONG_PTR) GetProcAddress(
         GetModuleHandle(_T("kernel32.dll")),
         "ReadProcessMemory");

      BYTE Instr = 0xB8;

      WriteProcessMemory(hProcess, (LPVOID) Addr, &Instr,
         sizeof (BYTE), NULL);

      Addr++;

      ULONG_PTR Rva = GetExportRva(Buffer, "_FakeReadProcessMemory@20");

      ULONG_PTR HookVA = BaseAddress + Rva;

      WriteProcessMemory(hProcess, (LPVOID) Addr, &HookVA,
         sizeof (ULONG_PTR), NULL);

      Addr += sizeof (ULONG_PTR);

      WORD Instr2 = 0xE0FF;

      WriteProcessMemory(hProcess, (LPVOID) Addr, &Instr2,
         sizeof (WORD), NULL);

      //
      // hook VirtualQueryEx
      //

      Addr = (ULONG_PTR) GetProcAddress(
         GetModuleHandle(_T("kernel32.dll")),
         "VirtualQueryEx");

      WriteProcessMemory(hProcess, (LPVOID) Addr, &Instr,
         sizeof (BYTE), NULL);

      Addr++;

      Rva = GetExportRva(Buffer, "_FakeVirtualQueryEx@16");

      HookVA = BaseAddress + Rva;

      WriteProcessMemory(hProcess, (LPVOID) Addr, &HookVA,
         sizeof (ULONG_PTR), NULL);

      Addr += sizeof (ULONG_PTR);

      WriteProcessMemory(hProcess, (LPVOID) Addr, &Instr2,
         sizeof (WORD), NULL);

      //
      // hook WriteProcessMemory
      //

      Addr = (ULONG_PTR) GetProcAddress(
         GetModuleHandle(_T("kernel32.dll")),
         "WriteProcessMemory");

      WriteProcessMemory(hProcess, (LPVOID) Addr, &Instr,
         sizeof (BYTE), NULL);

      Addr++;

      Rva = GetExportRva(Buffer, "_FakeWriteProcessMemory@20");

      HookVA = BaseAddress + Rva;

      WriteProcessMemory(hProcess, (LPVOID) Addr, &HookVA,
         sizeof (ULONG_PTR), NULL);

      Addr += sizeof (ULONG_PTR);

      WriteProcessMemory(hProcess, (LPVOID) Addr, &Instr2, %

TOP

              

[ Last edited by progholic on 2005-12-15 at 20:03 ]
附件
anticrack_ref.rar (23.21 KB)

2005-12-15 19:38, 下载次数: 324

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值