现代dump技术及保护措施[Ms-Rem]

读写内存源码(驱动级) 读写内存(驱动级),可直接读取程序(游戏)的内存或OD附加 立即下载

鄙人拙译

现代dump技术及保护措施

本文目的

我们都喜欢使用免费的软件,这也就意味着需要有人对它们进行破解。而破解的时候就要对付各种各样的壳和protectors。壳的工作原理和脱壳的基本方法在《Packer终结篇》(NEOx, Volodya)一文中有不错的讲解。在此文中详细的讲解了PE文件格式以及protectors对它的利用方法。无疑,脱壳过程中一个重要的部分就是取得dump。关于dump的取得以及反dump的方法在那些文章中也有介绍,而所有讲到的这些PE Tools中的anti-dumping的方法都有绕过的办法。但是,遗憾的是,所有这些只能用于简单的保护,而更为复杂的protectors(eXtreme Protector, Armadillo)都使用了完全不同的反dump的方法,PE Tools已经是相形见绌了。在本文中,我想来研究一下现代的反dump方法以及绕过的办法。这对于那些想学习如何脱掉比ASPack更复杂的壳的人们来说无疑是有用的。

ring 0下的anti-dump

所有的进程dumpers都是由OpenProcess/ReadProcessMemory/VirtualQueryEx等函数来实现的。为了取得进程加载的模块列表通常要使用ToolHelp API,ToolHelp API函数使用ReadProcessMemory来读取进程的内存。在NativeAPI级别上调用的是函数ZwOpenProcess和ZwReadVirtualMemory。一个很显然的反dump的方法就是创建驱动程序,利用驱动在内核模式下拦截这些函数并禁止它们对所保护的进程进行访问。

最简单的办法就是只拦截ZwOpenProcess函数,因为要读取进程的内存首先得把进程打开。HOOK的handler一般是这样的:

NTSTATUS NewNtOpenProcess (
    OUT PHANDLE ProcessHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes,
    IN PCLIENT_ID ClientId OPTIONAL)
{
    HANDLE ProcessId;

    if ((ULONG)ClientId > *MmUserProbeAddress) return STATUS_INVALID_PARAMETER;
    __try
    {
        ProcessId = ClientId->UniqueProcess;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        DPRINT("Exception");
        return STATUS_INVALID_PARAMETER;
    }

    if (IsAdded(wLastItem, ProcessId)) 
    {
        DPRINT("Access Denied!");
        return STATUS_ACCESS_DENIED;
    } else 
    
    return TrueNtOpenProcess(ProcessHandle, DesiredAccess, 
                             ObjectAttributes, ClientId);
}

这段代码首先用保险的办法取出要打开的进程的ProcessID,然后判断是该传递控制权还是返回STATUS_ACCESS_DENIED。因为系统中受保护的进程可能不止一个,所以必须建立进程的列表并向其中添加新的进程并删除已经搞定的进程。为了建立这个列表我们来讲一下用来描述受保护进程的结构体:

typedef struct _ProcessList
{
    PVOID NextItem;
    HANDLE Pid;
} TProcessList, *PProcessList;

下面是管理结构体链表的代码:

BOOLEAN IsAdded(PProcessList List, HANDLE Pid)
{
    PProcessList Item = List;
    while (Item)
    {
        if (Pid == Item->Pid) return TRUE;
        Item = Item->NextItem;
    }
    return FALSE;
}

void DelItem(PProcessList *List, HANDLE Pid)
{
    PProcessList Item = *List;
    PProcessList Prev = NULL;
    while (Item)
    {
        if (Pid == Item->Pid)
        {
                   if (Prev) Prev->NextItem =
                     Item->NextItem; else *List = Item->NextItem;
                   ExFreePool(Item);
                   return;
        }
        Prev = Item;
        Item = Item->NextItem;
    }
    return;
}


void FreePointers(PProcessList List)
{
    PProcessList Item = List;
    PVOID Mem;
    while (Item)
    {
        Mem = Item;        
        Item = Item->NextItem;
        ExFreePool(Mem);
    }
    return;
}


void AddItem(PProcessList *List, HANDLE Pid)
{
    PProcessList wNewItem;
    wNewItem = ExAllocatePool(NonPagedPool, sizeof(TProcessList));
    wNewItem->NextItem = *List;
    *List = wNewItem;
    wNewItem->Pid = Pid;
    return;
}

当发现新进程时,为了向链表中添加此进程我们将使用IOCTL向驱动程序发出请求,而完成后则用同样的办法将其删除。实现这种anti-dump的驱动程序的完整代码位于本文的附件程序中。

更为可靠的保护就是拦截ZwReadVirtualMemory/ZwWriteVirtualMemory和ZwCreateThread,但是这样就必须通过进程的句柄来取得它的ProcessID。为此可以使用ZwQueryInformationProcess函数,但是对于那些有PROCESS_QUERY_INFORMATION访问标志的句柄来说这招就不灵了,
所以最好是使用ObReferenceObjectByHandle来取得EPROCESS结构体指针,然后直接从EPROCESS结构体中取出ProcessID。代码如下:

ULONG GetPid(HANDLE PHanlde)
{
    NTSTATUS st = 15; 
    PEPROCESS process = 0;
    ULONG pId;
    
    st = ObReferenceObjectByHandle(PHanlde, 0, NULL, UserMode, &process, NULL);

    if (st == STATUS_SUCCESS)
    {
        pId = *(PULONG)(((ULONG)process) + pIdOffset);

        ObDereferenceObject(process);

        return pId;
    }
    return 0;
}

pIdOffset - ProcessId在EPROCESS结构体中的偏移量,这个constant在不同版本系统的内核中是不一样的,所以在驱动启动的时候要检查内核的版本并相应的为变量赋值。在写handler时要考虑到,如果进程试图用ZwOpenProcess来打开自己时也应该能工作(比较PsGetCurrentProcessId),而在处理接收句柄的函数时要考虑到用于表示当前进程的伪句柄(-1)的存在性。

说起理论来都挺容易,但实际实现起来却是一路的磕磕绊绊。比如,在Windows XP中有style服务。为了实现它需要允许子系统服务进程(csrss.exe)访问受保护进程的内存。由此就产生了问题——如何确定这个进程的Id?这个问题初看上去好像简单,其实不然。用进程名来确定是不行的,因为系统中可能有不止一个的csrss.exe进程(例如,cracker可以给自己的dumper也取这个名字),所以还需要更为可靠的唯一确定进程的方法。为此我决定使用这样的办法,子系统sever拥有某些命名对象,通过这些命名对象可以确定这个进程。例如,我们取LPC端口/Windows/ApiPort,在所有版本的Windows NT上它都是由csrss创建的。为了确定它需要使用ZwQuerySystemInformation枚举所有已打开的句柄,将它们拷贝到内核句柄表中,调用ZwQueryObject并比较所取得的名称与所寻找的是否一致。若一致,则拥有此句柄的进程的Id就是csrss的。下面是实现代码:

PVOID GetInfoTable(ULONG ATableType)
{
  ULONG mSize = 0x4000;
  PVOID mPtr = NULL;
  NTSTATUS St;

  do
  {
    mPtr = ExAllocatePool(PagedPool, mSize);

    if (mPtr != NULL)
    {
      St = ZwQuerySystemInformation(ATableType, mPtr, mSize, NULL);

    } else return NULL;

    if (St == STATUS_INFO_LENGTH_MISMATCH)
    {
      ExFreePool(mPtr);
      mSize = mSize * 2;
    }

  } while (St == STATUS_INFO_LENGTH_MISMATCH);

  if (St == STATUS_SUCCESS) 
  {
    DPRINT("GetInfoTable Success!");
    DPRINT("Info table in memory size - %d", mSize);
    return mPtr; 

  } else ExFreePool(mPtr);

  DPRINT("Error on GetInfoTable %X", St);

  return NULL;
}

ULONG GetCsrPid()
{
int r;
HANDLE Process, hObject;
NTSTATUS St;
ULONG CsrId = 0;
OBJECT_ATTRIBUTES obj;
CLIENT_ID cid;
POBJECT_NAME_INFORMATION ObjName;
UNICODE_STRING ApiPortName;
  
  PSYSTEM_HANDLE_INFORMATION_EX Handles;

  RtlInitUnicodeString(&ApiPortName, L"//Windows//ApiPort");

  DPRINT("Get handles info");

  Handles = GetInfoTable(SystemHandleInformation);

  if (Handles == NULL) return 0;

  ObjName = ExAllocatePool(PagedPool, 0x2000);

  DPRINT("Number of handles %d", Handles->NumberOfHandles);

  for (r = 0; r != Handles->NumberOfHandles; r++)
  {
    if (Handles->Information[r].ObjectTypeNumber == 21) //Port object
    {
  InitializeObjectAttributes(&obj, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
      
      cid.UniqueProcess = (HANDLE)Handles->Information[r].ProcessId;
      cid.UniqueThread  = 0;

      if (ZwOpenProcess(&Process, PROCESS_DUP_HANDLE, &obj, &cid)
             == STATUS_SUCCESS)
      {
        if (ZwDuplicateObject(Process, 
            (HANDLE)Handles->Information[r].Handle,
            NtCurrentProcess(), &hObject, 
            0, 0, DUPLICATE_SAME_ACCESS) == STATUS_SUCCESS)
        {
          if (ZwQueryObject(hObject, ObjectNameInformation, 
                     ObjName, 0x2000, NULL) == STATUS_SUCCESS)
          {
            if (ObjName->Name.Buffer != NULL)
            if (wcsncmp(ApiPortName.Buffer, ObjName->Name.Buffer, 20) == 0)
            {
              DPRINT("Csrss %d", Handles->Information[r].ProcessId);
              DPRINT("csr port - %ws", ObjName->Name.Buffer);
              CsrId = Handles->Information[r].ProcessId;
              ZwClose(Process);
              ZwClose(hObject);
              CsrId = Handles->Information[r].ProcessId;
              ExFreePool(Handles);
              ExFreePool(ObjName);
              return CsrId;
            } 
          } else DPRINT("Error in Query Object");
          ZwClose(hObject);
        } else DPRINT("Error on duplicating object");
        ZwClose(Process);
      } else DPRINT("Could not open process");  
    }
  }

  ExFreePool(Handles);
  ExFreePool(ObjName);

  return 0;
}

Themida (Extreme Protector) 使用的就是这种方法,在早期版本中为了处理好GUI的问题,使用的就是上面介绍的办法,但在后来的版本中却放弃了它(使得保护效果变差),新版的Themida并未禁止对受保护进程全部地址空间的访问,而只是禁止了对EXE文件所驻留的内存范围的访问,而所有加载到内存里的DLL未收到任何的保护。

在本文所附的程序中可以找到编写好的驱动程序,这个驱动程序可以保护进程不会被用这种方法dump出来。如果有人想把这个驱动添加到自己的protector里并开始大卖自己的Xprot的话,我会感到悲哀的——这种方法已经过时了,这里讲到它只是为了让文章完整。

当然,这种方法看上去还是相当好的,因为大多数cracker都不在内核模式下进行创作。摘除这种保护的办法之一就是找到原始处理程序的地址,patch掉SDT摘除HOOK。问题在于,这种方法并不通用,因为HOOK不只是能通过SDT建立,还可以通过拦截int 2Eh (win2000)或是修改sysenter (win XP)的处理程序,再有就是可以对原始处理程序的代码进行splicing。摘除protector的HOOK并不是最好的办法,因为protector可以检查HOOK是否还存在,而且还会采取措施来阻止HOOK被摘除(StarForce就是这样),所以我给出另一种方法——不在驱动程序中使用ZwReadVirtualMermory来进行dump。

在Windows NT内核中有两个未公开的函数KeAttachProcess/KeDetachProcess,驱动程序可以调用这两个函数来改变当前地址空间。函数原型如下:

extern
void KeAttachProcess(PEPROCESS Process);

extern
void KeDetachProcess(void);

通过这两个函数可以attach到受保护的进程并读取它的内存,但遗憾的是最新版的Themida对这俩个函数进行了patch,禁止用这种方法来dump自己的进程。但是它的保护并不全面,因为能attach到别的进程的函数还有KeStackAttachProcess/KeUnstackDetachProcess,而这两个函数却常被protectors遗忘(也容易产生问题,因为系统驱动程序会用到这两个函数),所以我们就用它们来读取内存。以下是函数原型和用到的结构体:

typedef struct _KAPC_STATE 
{
  LIST_ENTRY ApcListHead[2];
  PVOID Process;
  BOOLEAN KernelApcInProgress;
  BOOLEAN KernelApcPending;
  BOOLEAN UserApcPending;
} KAPC_STATE, *PKAPC_STATE;

extern 
NTKERNELAPI void KeStackAttachProcess(PVOID Process, PKAPC_STATE ApcState);

extern 
NTKERNELAPI void KeUnstackDetachProcess(PKAPC_STATE ApcState);

即使下一版的Themida实现了对这两个函数的拦截,attach到所需的进程也不难,手动修改cr3寄存器就行了。

为了读取进程的内存,我们需要使用IOCTL向我们的驱动程序传递进程的句柄、要读取的内存地址、当前进程缓冲区的地址以及要读取的字节数,在驱动获得dump进程的EPROCESS指针之后就会读取请求的数据。也就是说,实际上我们自行手工实现了ZwReadVirtualMemory。代码如下:

void CopyProcessMem(HANDLE hProcess, PVOID SrcAddr, PVOID DstAddr, ULONG *Size)
{
    PEPROCESS process = NULL;
    NTSTATUS  st;
    PUCHAR pMem = NULL;
    ULONG Addr, Bytes;
    PUCHAR cPtr, dPtr;
    KAPC_STATE ApcState;

    st = ObReferenceObjectByHandle(hProcess, 0, NULL, UserMode, &process, NULL);

    if (NT_SUCCESS(st))
    {    
        Bytes = *Size;
        pMem = ExAllocatePool(NonPagedPool, Bytes);
        dPtr = pMem;

        cPtr = (PUCHAR)SrcAddr;

        KeStackAttachProcess(process, &ApcState);
    
        __try
        {
            while (Bytes)
            {
                *dPtr = *cPtr;
                cPtr++;
                dPtr++;
                Bytes --;
            }
        }

        __except(EXCEPTION_EXECUTE_HANDLER)
        {
        }

        KeUnstackDetachProcess(&ApcState);

        Bytes = *Size - Bytes;

        __try
        {
            memcpy(DstAddr, pMem, Bytes);
            *Size = Bytes;
        }
        __except(EXCEPTION_EXECUTE_HANDLER)
        {
        }

        ExFreePool(pMem);

        ObDereferenceObject(process);
    }
    return;
}

对于驱动方式的dumping来说,要建立针对它的保护措施就要难多了,因为实际上来讲很难确定传递的信息究竟是来自于dumper还是来自于系统。但也不是不可能做到,您不要以为这招可以对付所有使用驱动dump的protectors(尽管目前还是有用的)。

遗憾的是,自行替换ZwWriteVirtualMemory在很多情况下是不够的,因为有些protectors还拦截了ZwOpenProcess。要绕过它,可以不去打开进程,而是通过在子系统的server进程里搜索来获得它的句柄(参见《使用Code Injection拦截Windows NT API函数》)。在我的advApiHook库里,对应这项功能的函数叫OpenProcessEx。有些保护程序会修改进程的security marker,以此来禁止打开内存读取。对这种保护,这个方法也可以对付。如果这种获取进程句柄的方法流行起来的话,保护程序的作者们就会最先在子系统server中把句柄关闭。要在驱动程序中获取进程句柄,只需使用PsLookupProcessByProcessId来取得指向其EPROCESS的指针,此后可以用ObOpenObjectByPointer把它加入到我们进程的句柄表中。实现代码如下:


HANDLE MyOpenProcess(HANDLE ProcessId)
{
    PEPROCESS Process;
    NTSTATUS St;
    HANDLE hProcess = NULL;

    PsLookupProcessByProcessId(ProcessId, &Process);

    ObOpenObjectByPointer(Process, 0, NULL, 0, NULL, UserMode, &hProcess);

    ObDereferenceObject(Process);

    return hProcess;
}

保护程序的作者们当然可以拦截PsLookupProcessByProcessId,但这对于获取dump只能起到5分钟的拖延作用,因为句柄的获取还可以通过手工挖掘内核结构体来完成,或者干脆就不用句柄也不用API。

ring 0下的保护手段就丰富多彩了(想象无限,创意无限)。例如,一个非常不错的anti-dump的方法就是破坏受保护进程的页表。为此需要干预调度器的工作并拦截某些SwapContext的未导出函数,这些函数都是在线程切换时要调用的。在拦截处理程序中,在切换入受保护进程时需要恢复页表,而在切换出时就把页表破坏掉。更为简单的是可以对抗dumper驱动。绕过这种保护也不困难,既可以在内核中进行(拦截中断处理程序),也可以在用户模式下进行(Code Injection)。我想,只要肯动脑,大家都能想出进行dump的自己原创的方法。 

Dynamic unpacking

另一种对付dump的常用方法就是的dynamic packing。其思想就是,protector并不将受保护的程序完全unpack,而只是unpack一部分。首先unpack第一页,当快要进行完时protector对异常进行拦截并unpack所请求的页,这时它就可以将上一页从内存中删除。这样受保护进程的image在内存里从来都不曾完整过,因此一般的dumper是没法进行dump的。这种办法在protector程序Armadillo中被广泛使用,被其称为CopyMem。为了拦截异常并对代码进行解码,程序建立了独立的进程,这个进程使用DebugAPI来调试受护进程。

对于摘除这种保护,大多数cracker采用的方法都不是最优的。他们的方法就是reverse并patch掉protector的代码,以此来迫使它解出完整的代码。这实在是太费事了,更让人难以接受的是,在每个新版本中,作者们都会修改这种保护机制的代码,这样旧的dump法就不再有效,从而就被人们毫不犹豫地扔到博物馆里。但幸运的是,我们还有更简单的办法——从内部来dump进程。为此需要将dump的代码注入到受保护进程的地址空间中并读取它的内存。在此过程中将会出现异常,处理异常的时候protector会向我们呈现全部解密后的代码。这种摘除CopyMem保护的方法已经是可以想出的最简单的办法了。CopyMem至今仍在被使用,原因就是这种方法还没有广泛流行。

获取kernel module的dump

在逆向ring 0下的protector的时候经常需要dump已加载到核心内存的PE文件。例如,如果protector对系统内核进行了patch,我们可以将内核的dump与磁盘文件做个比较,从而很容易地发现这个情况。抑或是dump出protector的驱动程序并研究它的解密方法。就是做到这些就已经不太容易了,因为所有现在的protectors早已经学会了在所有系统结构体中删除对自身驱动的引用,以此来实现驱动的隐藏,所以dump这种驱动的最现实的办法就是dump所有核心内存,之后在此dump中查找我们需要的东西。但是这个办法常常又不是很有效,因为保护程序的设计者们已经停止了对驱动的pack(比如StarForce 3),而是转向了更有前景的保护方法,比如多态代码以及虚拟机。

初看上去,dump内核模块只需要用copymem进行拷贝就行了,但实际上并没有这么简单,因为这里存在着一个陷阱。若要试图从头到尾读取模块内存的话,所能得到的就只是个蓝屏。若要尝试为所需内存段创建MDL并执行MmProbeAndLockPages,则会引发异常,而如果调用MmBuildMdlForNonPagedPool,则MDL会被建立,但要读取它会再一次引发蓝屏。与此相关的是,在native PE文件里,section可以有个“Discardable as needed”属性,这样的section在Driver Entry结束后会被立即删除。这样的系统可以节约NonPaged Pool,因为NonPaged Pool在系统里可是稀缺的。于是尝试访问这个地址就会引发异常,这个异常SEH不予处理,于是引起系统的崩溃。所以在读取内存之前我们需要用MmIsAddressValid来验证地址的有效性。以下是安全读取核心内存的代码:

NTSTATUS DumpKernelMemory(PVOID SrcAddr, PVOID DstAddr, ULONG Size)
{
    PMDL  pSrcMdl, pDstMdl;
    PUCHAR pAddress, pDstDddress;
    NTSTATUS st = STATUS_UNSUCCESSFUL;
    ULONG r;

    pSrcMdl = IoAllocateMdl(SrcAddr, Size, FALSE, FALSE, NULL);

    if (pSrcMdl)
    {
        MmBuildMdlForNonPagedPool(pSrcMdl);

        pAddress = MmGetSystemAddressForMdlSafe(pSrcMdl, NormalPagePriority);

        if (pAddress)
        {
            pDstMdl = IoAllocateMdl(DstAddr, Size, FALSE, FALSE, NULL);

            if (pDstMdl)
            {
                __try
                {
                    MmProbeAndLockPages(pDstMdl, UserMode, IoWriteAccess);

                    pDstDddress = MmGetSystemAddressForMdlSafe(
                                     pDstMdl, NormalPagePriority);

                    if (pDstDddress)
                    {
                        memset(pDstDddress, 0, Size);

                        for (r = 1; r < Size; r++)
                        {
                            if (MmIsAddressValid(pAddress)) *pDstDddress = *pAddress;
                            pAddress++;
                            pDstDddress++;
                        }

                        st = STATUS_SUCCESS;
                    }
                    
                    MmUnlockPages(pDstMdl);
                }

                __except(EXCEPTION_EXECUTE_HANDLER)
                {                    
                }

                IoFreeMdl(pDstMdl);
            }
        }            

        IoFreeMdl(pSrcMdl);
    }

    return st;
}

除内存读取之外,我们还需要获取模块的信息来知道读取的地址。获取此信息的方法将在后面介绍。


使dump过程更为便利

编写自己的dumper绝对是个费事的活儿,所以我决定使用PE Tools,因为它考虑了许多PE文件的细节并能方便快捷地进行dump。所以我写了一个叫eXtreme dumper(与Extreme Protector相对)的plugin,这个plugin可以实现驱动dump和使用DllInjection的dump。为此我在PE Tools中拦截了ZwOpenProcess和ZwReadVirtualMemory函数并用自己的函数做了替换,它们都是用上面介绍的方法写成的。作为例子,我这里给出ZwReadVirtualMemory处理程序的代码:

function NewZwReadVirtualMemory(ProcessHandle: dword; BaseAddress: pointer;
                                Buffer: pointer; BufferLength: dword;
                                ReturnLength: pdword): NTStatus; stdcall;
var
 hPipe, Bytes, Len: dword;
 Req: TXDumpRequest;
 PipeName: array [0..128] of Char;
 sPid: array [0..8] of Char;
 ProcessId: dword;
 Query: TDriverQuery;
begin
 if DriverMethod then
  begin
    Result := dword(-1);
    Len := BufferLength;
    Query.QueryType := 2;
    Query.Param1    := ProcessHandle;
    Query.Param2    := dword(@Len);
    Query.Param3    := dword(BaseAddress);
    Query.Param4    := dword(Buffer);
    WriteFile(hDriver, Query, SizeOf(TDriverQuery), Bytes, nil);
    if ReturnLength <> nil then ReturnLength^ := Len;
    if Len > 0 then Result := STATUS_SUCCESS;
  end else
  begin
   ProcessId := GetPid(ProcessHandle);
   StrCpy(PipeName, bPipeName);
   ToHex(ProcessId, 8, sPid);
   StrCat(PipeName, sPid);
   hPipe := CreateFile(@PipeName,
                     GENERIC_WRITE or GENERIC_READ,
                     0, nil, OPEN_EXISTING, 0, 0);
   if hPipe <> INVALID_HANDLE_VALUE then
     begin
      Req.Address := BaseAddress;
      Req.Length  := BufferLength;
      WriteFile(hPipe, Req, SizeOf(TXDumpRequest), Bytes, nil);
      ReadFile(hPipe, Len, SizeOf(dword), Bytes, nil);
      ReadFile(hPipe, Buffer^, Len, Bytes, nil);
      if ReturnLength <> nil then ReturnLength^ := Len;
      Result := 0;
      CloseHandle(hPipe);
     end else Result := TrueZwReadVirtualMemory(ProcessHandle,
                                      BaseAddress, Buffer,
                                      BufferLength, ReturnLength);
  end;
end;

我们看到,为了使dumper与注入的DLL互动在受保护的进程中使用了named pipes,其名字就是字符串eXtremeDumper和进程Id的HEX代码。读取内存的请求发向被dump的进程,在那里server端读取请求的内存并将数据和读取的字节数返回。dumper的server端的代码如下:

library xDump;

uses
  Windows,
  advApiHook,
  NativeApi;

{$include string.inc}

type
 PXDumpRequest = ^TXDumpRequest;
 TXDumpRequest = packed record
  ReqType: dword;
  Address: pointer;
  Length: dword;
 end;

const
 bPipeName = '//./pipe/eXtremeDumper'#0;

function SafeReadMemory(Addr, Buff: pointer; Size: dword): dword;
asm
 push ebx
 push edx
 push ecx
 push esi
 push edi
 push ebp
 push offset @Handler
 push fs:[0]
 mov fs:[0], esp
 mov esi, eax
 mov edi, edx
 rep movsb
 mov eax, ecx
 pop fs:[0]
 add esp, 4
 pop ebp
 pop edi
 pop esi
 pop ecx
 pop edx
 pop ebx
 ret
@handler:
 mov ecx, [esp + $0C] 
 add [ecx + $B8], 2
 ret
end;

function PipeThread(hPipe: dword): dword; stdcall;
var
 Req: TXDumpRequest;
 Bytes, Len: dword;
 pBuff: pointer;
begin
  ReadFile(hPipe, Req, SizeOf(TXDumpRequest), Bytes, nil);
  GetMem(pBuff, Req.Length);
  Len := Req.Length - SafeReadMemory(Req.Address, pBuff, Req.Length);
  WriteFile(hPipe, Len, SizeOf(dword), Bytes, nil);
  WriteFile(hPipe, pBuff^, Req.Length, Bytes, nil);
  FreeMem(pBuff);
  CloseHandle(hPipe);
end;

var
 TrId: dword;

procedure PipeServerThread();
var
 hPipe: dword;
 PipeName: array [0..128] of Char;
 sPid: array [0..8] of Char;
begin
 StrCpy(PipeName, bPipeName);
 ToHex(GetCurrentProcessId(), 8, sPid);
 StrCat(PipeName, sPid);
 repeat
  hPipe := CreateNamedPipe(PipeName,
                           PIPE_ACCESS_DUPLEX or WRITE_DAC,
                           PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT,
                           PIPE_UNLIMITED_INSTANCES, 1024, 1024, 5000, nil);
  if hPipe = INVALID_HANDLE_VALUE then Exit;
  if ConnectNamedPipe(hPipe, nil) then 
  CreateThread(nil, 0, @PipeThread, pointer(hPipe), 0, TrId);
 until false;
end;

begin
 CreateThread(nil, 0, @PipeServerThread, nil, 0, TrId);
end.

在client端连接pipe时会创建一个线程,该线程在SEH处理程序内部读取请求的数据,此后数据就流向client端。此代码唯一的缺点就是效率比较低,因为每个请求都是由单独的线程服务的。但是,实际上太高的效率也是多余,因为重要的是dump的获取,尽管有人会不满足,会编写更好的版本,但我这里就费点傻劲吧 :)。

现在我们回到内核模块信息的获取上来。为了实现这个目的,我将使用函数ZwQuerySystemInformation,class为SystemModuleInformation,这样函数会向我返回以下类型的结构体数组:

 PSYSTEM_MODULE_INFORMATION = ^SYSTEM_MODULE_INFORMATION;
 SYSTEM_MODULE_INFORMATION = packed record // Information Class 11
    Reserved: array[0..1] of ULONG;
    Base: PVOID;
    Size: ULONG;
    Flags: ULONG;
    Index: USHORT;
    Unknown: USHORT;
    LoadCount: USHORT;
    ModuleNameOffset: USHORT;
    ImageName: array [0..255] of Char;
   end;

 PSYSTEM_MODULE_INFORMATION_EX = ^SYSTEM_MODULE_INFORMATION_EX;
 SYSTEM_MODULE_INFORMATION_EX = packed record
    ModulesCount: dword;
    Modules: array[0..0] of SYSTEM_MODULE_INFORMATION;
   end;

我们感兴趣的域是ImageName、Base和Size。我发现,在KernelMode下还可以通过链表PsLoadedModulesList来获取这项信息,但是用在这里就没什么意义了。

为了枚举进程加载的模块,PE Tools使用了Procs32.dll中的函数GetModuleFirst/GetModuleNext。为了映射自己的列表,我们将在System进程zhong拦截并替换成我们的版本。处理这些函数的代码如下:

function NewGetModuleNext(dwPID: dword; mEntry: PMODULE_ENTRY): bool; stdcall;
begin
  if dwPID = SystemPid then
   begin
     Result := false;
     lstrcpy(mEntry^.lpFileName, Modules^.Modules[CurrentModule].ImageName);
     mEntry^.dwImageBase := dword(Modules^.Modules[CurrentModule].Base);
     mEntry^.dwImageSize := Modules^.Modules[CurrentModule].Size;
     mEntry^.bSystemProcess := true;
     Inc(CurrentModule);
     if CurrentModule > Modules^.ModulesCount then
      ReleaseModulesInfo() else Result := true;
   end else Result := TrueGetModuleNext(dwPID, mEntry);
end;

function NewGetModuleFirst(dwPID: dword; mEntry: PMODULE_ENTRY): bool; stdcall;
begin
  if dwPID = SystemPid then
   begin
     if CurrentModule > 0 then ReleaseModulesInfo();
     Modules := GetInfoTable(SystemModuleInformation);
     Result := NewGetModuleNext(dwPID, mEntry);
   end else Result := TrueGetModuleFirst(dwPID, mEntry);
end;

为了进行dump,PE Tools使用了NDump.dll中的DumpProcess函数,我们需要拦截这个函数并将dump请求发向我们自己的驱动里。处理程序的代码如下:

function NewDumpProcess(dwProcessId: dword; pStartAddr: pointer;
                        dwcBytes: dword; pDumpedBytes: pointer
                        ): bool; stdcall;
var
 Query: TDriverQuery;
 Bytes: dword;
begin
  if dwProcessId = SystemPid then
   begin
    Query.QueryType := IOCTL_DUMP_KERNEL_MEM;
    Query.Param1    := dword(pStartAddr);
    Query.Param2    := dwcBytes;
    Query.Param3    := dword(pDumpedBytes);
    Result := WriteFile(hDriver, Query, SizeOf(TDriverQuery), Bytes, nil);
   end else Result := TrueDumpProcess(dwProcessId, pStartAddr,
                                      dwcBytes, pDumpedBytes);
end;

--------------------------------------------------------------------------------


eXtreme dumper可以分为以下部分:

两种dump的方法(Dll-Injection法和驱动法)
获取句柄的隐式方法(OpenProcessEx)
防止PE Tools受其它进程影响(Protect PE Tools)
对内核模块的dump(Enable kernel modules dumping)

简言之,这个plugin无疑是很有用的。

易语言-易语言驱动级读写内存模块 易语言驱动级读写内存模块源码例程程序调用API函数实现通过驱动读写内存数据。点评:易语言API操作驱动程序的应用例程。 三叶易语言自学网 立即下载

相关推荐

ExtremeDumper_extrimedumler_

extrime dumler for windows net asembly

【亲测免费】 探索 .NET 应用程序的利器:ExtremeDumper

探索 .NET 应用程序的利器:ExtremeDumper 在软件开发和调试过程中,有时我们需要深入了解 .NET 应用程序的内部结构。这就是 ExtremeDumper 进场的地方,一个强大而轻量级的.NET 库分析工具。它提供了直观的界面,可以轻松地查看进程中的 .NET 模块,并且能够处理复杂的反dump保护机制。 1、项目介绍 ExtremeDumper 是一款专为 .NET 开发者设计的...

gitblog_00037的博客 902

ExtremeDumper_.net_dumper_

.NET Assembly Dumper

用驱动实现自己的 ZwOpenProcess ZwReadVirtualMemory ZwWriteVirtualMemory

懒得自己写了,就转一个帖子吧。用驱动实现自己的 ZwOpenProcess ZwReadVirtualMemory ZwWriteVirtualMemory/* jhxxs.C Author: Last Updated: 2006-03-23 This framework is generated by EasySYS 0.3.0 This te

Tommy(凌飞)的专栏 5897

数据标注的下一代SOP,可能不是Word,而是一段Prompt

再给几个反例:“这个只是正常纹理,不算。真正用于数据生产的Prompt,可能至少包含:任务定义、正例、反例、边界案例、优先级、输出格式、验收逻辑。一个项目100个人,如果100个人对这句话有100种理解,最后返工的就不是一个框,而可能是几十万帧数据。一个项目启动以后,项目经理先研究SOP,培训组长,组长再培训标注员,最后几百个人按照同一套规则干活。实际上一个成熟的项目经理,最重要的能力之一,就是:把客户的业务语言翻译成生产规则。真正让项目难交付的,从来不是80%的普通数据,而是那20%的长尾数据。

AI数据标注猿 296

麒麟操作系统安装达梦数据库ARM版

Linux安装达梦数据库ARM版

运维@小兵的博客 8771

现代dump技术及保护措施[Ms-Rem](上)

鄙人拙译现代dump技术及保护措施本文目的我们都喜欢使用免费的软件,这也就意味着需要有人对它们进行破解。而破解的时候就要对付各种各样的壳和protectors。壳的工作原理和脱壳的基本方法在《Packer终结篇》(NEOx, Volodya)一文中有不错的讲解。在此文中详细的讲解了PE文件格式以及protectors对它的利用方法。无疑,脱壳过程中一个重要的部分就是取得dump。关于dump的取得

Tommy(凌飞)的专栏 838

Easymesh之拓扑模块(二)

介绍 easymesh 拓扑消息:Topo discovery、Topo notification、Topo query、Topo response

qq_53619687的博客 2154

现代dump技术及保护措施[Ms-Rem](下)

Dynamic unpacking另一种对付dump的常用方法就是的dynamic packing。其思想就是,protector并不将受保护的程序完全unpack,而只是unpack一部分。首先unpack第一页,当快要进行完时protector对异常进行拦截并unpack所请求的页,这时它就可以将上一页从内存中删除。这样受保护进程的image在内存里从来都不曾完整过,因此一般的dumper是没

Tommy(凌飞)的专栏 944

Unity 多线程编程笔记 _Task详解

士大夫

weixin_43405845的博客 4384

Avalonia 超级详细学习指南

什么是 Avalonia?Avalonia 是一个开源的、跨平台的 GUI 框架,允许开发者使用 XAML 和 C# 构建现代化、高性能的桌面应用程序。与其他框架相比,Avalonia 具有强大的跨平台能力和灵活的界面设计。} }()Avalonia 的特点和优势跨平台性:Avalonia 可以在 Windows、Linux、macOS 等多个平台上运行,开发者可以编写一套代码,然后在不同的操作系统上运行相同的应用程序。

仰望星空的博客 1万+

现代dump技术及保护措施

  现代dump技术及保护措施[Ms-Rem](上)现代dump技术及保护措施本文目的我们都喜欢使用免费的软件,这也就意味着需

1384

【SAP ME 3】JAVA 调用SAP PO WebService

Java调用SAP PO/PI WebService接口

探索 Java 编程的技巧、最佳实践和解决方案。本博客提供针对 Java 开发者的实用指南,涵盖从基础到高级话题,旨在提升编程效率和技能。 847

Windows内核情景分析学习笔记(二)

X86系统调用上 本文环境 WinXP SP2 在Windows操作系统中,应用层程序进行系统调用一般有两种途径,一种是通过int 0x2e的方式,一种是通过sysenter指令即快速调用进入系统调用。接下来将以ReadProcessMemory这个函数为例来介绍在X86下是如何进行系统调用通过的。主要讲解通过快速调用的方式进行系统调用 这是位于kernel32.dll中的ReadProcessMemory mov edi, edi push ebp mov ebp,

weixin_44356908的博客 377

通信算法之281:大疆DJI无人机ID-DJI DroneID开源工程-相关问题-协议信息问题

ZC序列的参数未知。但是,使用第一个或第二个零相关序列对每个OFDM符号进行相位调整,对于直接相邻于使用过的零相关序列的时间上的OFDM符号工作得很好,但对其它符号效果不佳。由于符号的顺序没有改变,而9 OFDM符号情况下的第一个OFDM符号没有用处,因此本存储库中的脚本对待所有突发都是有9个OFDM符号的,并且不对第一个OFDM符号做任何处理。棘手的地方在于需要生成比数据载波数量(始终为600)多1个的零相关序列,零化中间元素,将生成的样本映射到FFT的中心(采样率决定bin的数量),然后计算IFFT。

leegang12的专栏 1623

YOLO小目标检测优化:9种实用方案,准确率直接提升15%

小目标检测是个系统工程,没有银弹,但有明确的优化路径。优先搞数据。Copy-Paste + 提升分辨率,这俩加起来成本最低、收益最大,能解决80%的小目标问题。别一上来就改模型结构。调参走在改结构前面。锚框聚类、损失加权、增强优化,这些都是零成本的,先做满了再说改模型的事。P2层是大杀器,但不要轻易用。加了P2层,TensorRT部署、后处理、速度都要跟着调,工程化工作量翻倍。数据和训练做到位还不够,再加也不迟。滑动窗口是兜底的。能从训练端解决的问题,就不要拖到推理端。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 192

现代dump技术及保护措施(下)

现代dump技术及保护措施(下)Dynamic unpacking另一种对付dump的常用方法就是的 dynamic packing。其思想就是,protector并不将受保护的程序完全unpack,而只是unpack一部分。首先unpack第一页,当快要进行完时protector对异常进行拦截并unpack所请求的页,这时它就可以将上一页从内存中删除。这样受保护进程的image在内存里从来都不曾

FISH 的专栏 2115

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

API函数的调用过程(三环进0环)

kernel32.dll(ReadProcessMemory)只是简单调用了 ntdll.dll(NtReadProcessmemory) .text:7C92D9E0 public ZwReadVirtualMemory .text:7C92D9E0 ZwReadVirtualMemory proc near ; CODE XREF: Ld...

H888001的博客 958

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

ring0下读取用户进程内存(转)2010-05-30 21:32ring0下读取用户进程内存

ring0下读取用户进程内存(转)2010-05-30 21:32ring0下读取用户进程内存 这里只是简单的实现下读取进程空间的数据...写操做也是一样的....比较简单... 这样你就可以在内核读用户空间了....哈哈 大家都知道在用户态我们常用 kernel32.dll中的ReadProcessMemory来读 取进程...这个函数只是简单地对传入的参数进行处理然后调用了 ntdll.dll中的NtReadVirtualMemory/ZwReadVirtualMemo

hucaiyu2009的专栏 1481

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

x64内核实验5-API进0环

今天开始我们来分析系统api进0环的过程。

qq_43147121的博客 1250

Windows系统调用中API的3环部分

一、R3环API分析的重要性 Windows所提供给R3环的API,实质就是对操作系统接口的封装,其实现部分都是在R0实现的。 很多恶意程序会利用钩子来钩取这些API,从而达到截取内容,修改数据的意图。 现在我们使用olldbg对ReadProcessMemory进行跟踪分析,查看其在R3的实现,并根据我们的分析来重写一个ReadProcessMemory。 重写ReadProcessMe...

dz45693的专栏 2942

Detect-KeAttachProcess - 实时检测内核模块注入攻击

Detect-KeAttachProcess - 实时检测内核模块注入攻击 Detect-KeAttachProcess 是一个开源的 Windows 防火墙驱动程序,用于实时检测和阻止通过 KeAttachProcess API 发起的内核模块注入攻击。 什么是 Detect-KeAttachProcess? Detect-KeAttachProcess 是一款轻量级的防火墙驱动程序,它监控系统...

gitblog_00001的博客 557

[Windows 驱动开发] 清内存强杀进程

原理 Attach 到进程进行内存清零.这里提供了两种方法.原理是一样 KeAttachProcess方法 与 KeStackAttachProcess方法. 其中第一种属于旧方法了.根据MSDN所说API已经升级为了KeStackAttachProcess #include <ntifs.h> NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process); //未公开的进行导出即可 NTKERNELAPI VOID

LYSM 1727

浅析windows安全策略

理论:本篇我们将通过一个例子,来大致了解下windows的安全策略。实现windows安全性的安全组件和数据库有:1。安全引用监视器Security reference monitor (简称SRM)   如图,这是位于核心态windows执行体中的一个组件。它负责: 1)定义访问令牌数据结构来表示一个安全环境。2)执行对象的安全访问检查3)管理特权(用户权限)4) 生成所有的结果安全审计消息。

深之JohnChen的专栏 7316

对抗packer-AntiMida 1.0

AntiMida 1.0Click here to download the project files.This article is just for fun, dont take it too serious. Some time ago a friend (Littleluk) asked me if i was interested in helping him with the

1888
上一篇: 对付API-splicing的一种简单方法
下一篇: 跟踪Native API函数调用
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值