Detection of the hidden processes

【202503】Wan:开放且先进的大规模视频生成模型【1.3B、14B】【支持对无限长度视频进行稳定的Inference】【Wan中的主要计算开销来自注意力机制】 wan: Open and Advanced Large-Scale Video Generative ModelsWan:开放且先进的大规模视频生成模型Wan Team, Alibaba Group Wan 团队,Alibaba GroupThis report presents Wan, a comprehensive and open suite of video foundation models designed to push the boundaries of video generation 阅读详情

找到线程链表地址之后我们就可以非常简单地枚举出那些进程了,代码如下:

Code:

void ProcessListHead(PLIST_ENTRY ListHead)
{
  PLIST_ENTRY Item;

  if (ListHead)
  {
     Item = ListHead->Flink;

     while (Item != ListHead)
     {
        CollectProcess(*(PEPROCESS *)((ULONG)Item + WaitProcOffset));
        Item = Item->Flink;
     }
  }

  return;
}

CollectProcess是一个非常有用的函数,它可以增加一个进程到进程列表中去。


通过拦截系统调用得到进程列表。

任何一个进程都要通过API来和系统进行交互,而大多数交互都通过系统调用传递给了内核。当然,进程也可以不使用任何API而存在,但是这

样一来它也就不能做任何有用(或有害)的事情。一般而言,我们的思路是使用系统调用管理器拦截系统调用,然后得到管理器中当前进程的

EPROCESS指针。应该在某段时间收集指针列表,这个表不会包含信息收集时没有使用任何系统调用的进程(比如,进程的线程都处于等待状态

)。

Windows 2000平台使用2Eh中断进行系统调用,因此我们需要修改IDT中的相应的中断描述符来拦截系统调用,这就要用sidt指令得到IDT在内存

中的位置。该指令返回这样一个结构:

Code:

typedef struct _Idt
{
  USHORT Size;
  ULONG  Base;
} TIdt;


修改2Eh中断向量的代码如下:

Code:

void Set2kSyscallHook()
{
  TIdt Idt;
  __asm
  {
     pushad
     cli
     sidt [Idt]
     mov esi, NewSyscall
     mov ebx, Idt.Base
     xchg [ebx + 0x170], si
     rol esi, 0x10
     xchg [ebx + 0x176], si
     ror esi, 0x10
     mov OldSyscall, esi
     sti
     popad
  }
}


当然在卸载驱动之前还要保存原始状态的信息:

Code:

void Win2kSyscallUnhook()
{
  TIdt Idt;
  __asm
  {
     pushad
     cli
     sidt [Idt]
     mov esi, OldSyscall
     mov ebx, Idt.Base
     mov [ebx + 0x170], si
     rol esi, 0x10
     mov [ebx + 0x176], si
     sti
     xor eax, eax
     mov OldSyscall, eax
     popad
  }
}


Windows XP使用sysenter/sysexit指令(出现在Pentium 2处理器中)实现系统调用。这些指令的功能由model-specific registers(MSR)控制

。系统调用管理器的地址保存在MSR寄存器,SYSENTER_EIP_MSR(0x176)中。用rdmsr指令读取MSR寄存器,同时设置ECX = 要读取的寄存器的号

码,结果保存在两个积存器EDX:EAX中。在我们这里,SYSENTER_EIP_MSR积存器是32位积存器,所以EDX为0,EAX内是系统调用管理器的地址。

同样地,我们也可以用wrmsr指令写MSR积存器。有一个地方需要注意:当写32位MSR积存器的时候,EDX应该被清空,否则将引起异常并且导致

系统立即崩溃。

考虑到所有的事情之后,替代系统调用管理器的代码如下:

Code:

void SetXpSyscallHook()
{
  __asm
  {
     pushad
     mov ecx, 0x176
     rdmsr
     mov OldSyscall, eax
     mov eax, NewSyscall
     xor edx, edx
     wrmsr
     popad
  }
}


恢复原始的系统调用管理器代码:

Code:

void XpSyscallUnhook()
{
  __asm
  {
     pushad
     mov ecx, 0x176
     mov eax, OldSyscall
     xor edx, edx
     wrmsr
     xor eax, eax
     mov OldSyscall, eax
     popad
  }
}


Windows XP的另外一个特性是它既可以使用sysenter也可以使用int 2Eh来进行系统调用,所以我们要替换这两种情况下的系统调用管理器。

我们的新的系统调用管理器应该得到当前进程的EPROCESS指针,并且如果是一个新的进程,我们要把这个新的进程加到我们的进程列表中。
新的系统调用管理器代码如下:

Code:

void __declspec(naked) NewSyscall()
{
  __asm
  {
     pushad
     pushfd
     push fs
     mov di, 0x30
     mov fs, di
     mov eax, fs:[0x124]
     mov eax, [eax + 0x44]
     push eax
     call CollectProcess
     pop fs
     popfd
     popad
     jmp OldSyscall
  }
}


得到进程列表的这段代码应该在某个时间段内工作,所以我们有这样的问题:如果在列表中的进程被删除掉,在随后的时间内我们将保留一些

无效指针,结果就是检测隐藏进程失败或者导致系统BSOD。解决这个问题的办法是,用PsSetCreateProcessNotifyRoutine函数注册我们的回调

函数,这个回调函数将会在系统创建或者销毁一个进程的时候被调用。当进程被销毁时,我们也应该把它从我们的表中删除掉。
回调函数的原型如下:

Code:

VOID
(*PCREATE_PROCESS_NOTIFY_ROUTINE) (
   IN HANDLE  ParentId,
   IN HANDLE  ProcessId,
   IN BOOLEAN  Create
   );

安装回调函数的代码如下:

Code:

PsSetCreateProcessNotifyRoutine (NotifyRoutine, FALSE);

取消回调函数的代码:

Code:

PsSetCreateProcessNotifyRoutine (NotifyRoutine, TRUE);


这里有一个问题,回调函数总是在系统被销毁的时候创建,因此我们不可能直接在这个回调函数中删除进程列表中的相应进程。这样我们就要

用系统的work items,首先调用IoAllocateWorkItem函数为work item分配内存,然后调用IoQueueWorkItem函数(俄文翻译者kao注:这一句我

不太确定...)将任务放置到工作线程队列中。在处理过程中我们不仅仅从进程列表中删除掉已经终止的进程,而且还要加入新创建的线程。处

理代码如下:

Code:

void WorkItemProc(PDEVICE_OBJECT DeviceObject, PWorkItemStruct Data)
{
  KeWaitForSingleObject(Data->pEPROCESS, Executive, KernelMode, FALSE, NULL);

  DelItem(&wLastItem, Data->pEPROCESS);

  ObDereferenceObject(Data->pEPROCESS);

  IoFreeWorkItem(Data->IoWorkItem);

  ExFreePool(Data);

  return;
}


void NotifyRoutine(IN HANDLE  ParentId,
                  IN HANDLE  ProcessId,
                  IN BOOLEAN Create)
{
  PEPROCESS       process;
  PWorkItemStruct Data;

  if (Create)
  {
     PsLookupProcessByProcessId(ProcessId, &process);

     if (!IsAdded(wLastItem, process)) AddItem(&wLastItem, process);

     ObDereferenceObject(process);

  } else
  {
     process = PsGetCurrentProcess();
     
     ObReferenceObject(process);

     Data = ExAllocatePool(NonPagedPool, sizeof(TWorkItemStruct));

     Data->IoWorkItem = IoAllocateWorkItem(deviceObject);
    
     Data->pEPROCESS  = process;

     IoQueueWorkItem(Data->IoWorkItem, WorkItemProc, DelayedWorkQueue, Data);
  }

  return;
}


这是一个相对可靠的隐藏进程的检测方式,然而虽然没有进程能够不倚赖系统调用,但还是有一些进程可以在很长一段时间处于等待状态不进

行系统调用,我们无法检测出这样的进程。

只要想做,躲避开这种检测方式还是很容易的。想要做到这一点,那就需要改变隐藏进程的系统调用方式(重定向到另外一个中断或者GDT中的

调用门)。在Windows XP下做这个工作是相当简单的,因为可以给ntdll.dll中的KiFastSystemCall函数打补丁和创建一个相应的系统调用门。

在Windows 2000平台下就稍微有点难度了,因为int 2Eh调用分散遍及整个ntdll,但是找到并patch所有的地方也并不是很复杂。综上所述,依

赖于这种检测方式可不是聪明之举。

通过遍历句柄表得到进程列表。
如果你曾经尝试过利用删除PsActiveProcesses链表中的进程节点来隐藏进程,可能你会注意到当你调用ZwQuerySystemInformation函数枚举句

柄的时候,隐藏进程的句柄也会被枚举出来,并且还能被检测出它的ProcessId。这是因为为了方便枚举句柄,所有的句柄表都是由一个双向链

表HandleTableList维护的。Windows 2000下HANDLE_TABLE结构在链表中的偏移等于0x054,Windows XP下为0x01C,链表由

HandleTableListHead开始。HANDLE_TABLE结构包括它的宿主进程的指针(QuotaProcess),Windows 2000下这个偏移等于0x00C,Windows XP

下这个偏移为0x004。通过遍历这个句柄链表我们就可以构建进程列表了。

首先我们得找到HandleTableListHead。反汇编内核显示它的引用定位在函数的深处,所以前面我们用过的反汇编代码的方法已经不能在这里使

用了。要找到HeadleTableListHead,我们要注意到HandleTableListHead是一个全局的内核变量,因此它一定是在内核文件的某一个段

(Section)里面,并且HandleTableList的其他成员是在动态分配的内存中,所以总是受到内核地址空间的限制。根据这些,我们需要得到任

何一个进程的HandleTable的指针,然后遍历链表直到找到定位在这个内核地址空间的成员,那么这个成员就是HandleTableListHead了。

我们使用ZwQuerySystemInformation和SystemModuleInformation类计算系统内核的基址和大小。它将返回一个所有已经加载了的模块的描述符

表,并且这个表的第一个成员始终是"system"。综上所述,查找HandleTableListHead的代码如下:

Code:

void GetHandleTableListHead()
{
  PSYSTEM_MODULE_INformATION_EX Info = GetInfoTable(SystemModuleInformation);
  ULONG NtoskrnlBase = (ULONG)Info->Modules[0].Base;
  ULONG NtoskrnlSize = Info->Modules[0].Size;
  PHANDLE_TABLE HandleTable = *(PHANDLE_TABLE *)((ULONG)PsGetCurrentProcess() + HandleTableOffset);
  PLIST_ENTRY HandleTableList = (PLIST_ENTRY)((ULONG)HandleTable + HandleTableListOffset);
  PLIST_ENTRY CurrTable;

  ExFreePool(Info);

  for (CurrTable = HandleTableList->Flink;
       CurrTable != HandleTableList;
       CurrTable = CurrTable->Flink)
  {
     if ((ULONG)CurrTable > NtoskrnlBase && (ULONG)CurrTable < NtoskrnlBase + NtoskrnlSize)
     {
        HandleTableListHead = CurrTable;
        break;
     }
  }  
}

这段代码是非常通用的,它可以运行于任何Windows NT版本的系统上,并且不仅可以用来查找HandleTableListHead,也可以用于其他类似的结构。

得到HandleTableListHead地址后我们就可以遍历句柄表并基于这些信息来构建进程列表了。

Code:

void ScanHandleTablesList()
{
  PLIST_ENTRY CurrTable;
  PEPROCESS QuotaProcess;

  for (CurrTable =  HandleTableListHead->Flink;
       CurrTable != HandleTableListHead;
       CurrTable =  CurrTable->Flink)
  {
     QuotaProcess = *(PEPROCESS *)((PUCHAR)CurrTable - HandleTableListOffset + QuotaProcessOffset);
     if (QuotaProcess) CollectProcess(QuotaProcess);
  }
}

F-Secure Black Light和KProcCheck的最后一个版本用的就是这种检测方法。我想你将会很轻松地找到对付这种检测的方法。
通过扫描PspCidTable得到进程列表。

有一件有趣的事情需要注意:如果仅仅把进程节点从PsActiveProcesses链表中删除,它不能够防止使用API函数OpenProcess打开进程。这样就

有一种检测进程的方法就是尝试穷举Pid然后调用OpenProcess。我不推荐这个方法,因为它没有任何优点,我甚至想说这是一种“狗屁”方案

。不过它的存在意味着在系统中除了通过PsActiveProcesses得到进程列表之外还可以通过调用OpenProcess。当穷举ProcessId的时候我们会注

意到一个进程可以被几个不同的Pid打开,这暗示可能存在着有点像HANDLE_TABLE的另一个进程列表。为了证明这个的猜想,我们来看看

ZwOpenProcess函数:

Code:

PAGE:0049D59E ; NTSTATUS __stdcall NtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess,
                                                POBJECT_ATTRIBUTES ObjectAttributes,PCLIENT_ID ClientId)
PAGE:0049D59E                 public NtOpenProcess
PAGE:0049D59E NtOpenProcess   proc near            
PAGE:0049D59E
PAGE:0049D59E ProcessHandle   = dword ptr  4
PAGE:0049D59E DesiredAccess   = dword ptr  8
PAGE:0049D59E ObjectAttributes= dword ptr  0Ch
PAGE:0049D59E ClientId        = dword ptr  10h
PAGE:0049D59E
PAGE:0049D59E                 push    0C4h
PAGE:0049D5A3                 push    offset dword_413560 ; int
PAGE:0049D5A8                 call    sub_40BA92
PAGE:0049D5AD                 xor     esi, esi
PAGE:0049D5AF                 mov     [ebp-2Ch], esi
PAGE:0049D5B2                 xor     eax, eax
PAGE:0049D5B4                 lea     edi, [ebp-28h]
PAGE:0049D5B7                 stosd
PAGE:0049D5B8                 mov     eax, large fs:124h
PAGE:0049D5BE                 mov     al, [eax+140h]
PAGE:0049D5C4                 mov     [ebp-34h], al
PAGE:0049D5C7                 test    al, al
PAGE:0049D5C9                 jz      loc_4BE034
PAGE:0049D5CF                 mov     [ebp-4], esi
PAGE:0049D5D2                 mov     eax, MmUserProbeAddress
PAGE:0049D5D7                 mov     ecx, [ebp+8]
PAGE:0049D5DA                 cmp     ecx, eax
PAGE:0049D5DC                 jnb     loc_520CDE
PAGE:0049D5E2 loc_49D5E2:                            
PAGE:0049D5E2                 mov     eax, [ecx]
PAGE:0049D5E4                 mov     [ecx], eax
PAGE:0049D5E6                 mov     ebx, [ebp+10h]
PAGE:0049D5E9                 test    bl, 3
PAGE:0049D5EC                 jnz     loc_520CE5
PAGE:0049D5F2 loc_49D5F2:                          
PAGE:0049D5F2                 mov     eax, MmUserProbeAddress
PAGE:0049D5F7                 cmp     ebx, eax
PAGE:0049D5F9                 jnb     loc_520CEF
PAGE:0049D5FF loc_49D5FF:                          
PAGE:0049D5FF                 cmp     [ebx+8], esi
PAGE:0049D602                 setnz   byte ptr [ebp-1Ah]
PAGE:0049D606                 mov     ecx, [ebx+0Ch]
PAGE:0049D609                 mov     [ebp-38h], ecx
PAGE:0049D60C                 mov     ecx, [ebp+14h]
PAGE:0049D60F                 cmp     ecx, esi
PAGE:0049D611                 jz      loc_4CCB88
PAGE:0049D617                 test    cl, 3
PAGE:0049D61A                 jnz     loc_520CFB
PAGE:0049D620 loc_49D620:                          
PAGE:0049D620                 cmp     ecx, eax
PAGE:0049D622                 jnb     loc_520D0D
PAGE:0049D628 loc_49D628:
PAGE:0049D628                 mov     eax, [ecx]
PAGE:0049D62A                 mov     [ebp-2Ch], eax
PAGE:0049D62D                 mov     eax, [ecx+4]
PAGE:0049D630                 mov     [ebp-28h], eax
PAGE:0049D633                 mov     byte ptr [ebp-19h], 1
PAGE:0049D637 loc_49D637:                    
PAGE:0049D637                 or      dword ptr [ebp-4], 0FFFFFFFFh
PAGE:0049D63B loc_49D63B:                    
PAGE:0049D63B                                      
PAGE:0049D63B                 cmp     byte ptr [ebp-1Ah], 0
PAGE:0049D63F                 jnz     loc_520D34
PAGE:0049D645 loc_49D645:                          
PAGE:0049D645                 mov     eax, PsProcessType
PAGE:0049D64A                 add     eax, 68h
PAGE:0049D64D                 push    eax
PAGE:0049D64E                 push    dword ptr [ebp+0Ch]
PAGE:0049D651                 lea     eax, [ebp-0D4h]
PAGE:0049D657                 push    eax
PAGE:0049D658                 lea     eax, [ebp-0B8h]
PAGE:0049D65E                 push    eax
PAGE:0049D65F                 call    SeCreateAccessState
PAGE:0049D664                 cmp     eax, esi
PAGE:0049D666                 jl      loc_49D718
PAGE:0049D66C                 push    dword ptr [ebp-34h] ; PreviousMode
PAGE:0049D66F                 push    ds:stru_5B6978.HighPart
PAGE:0049D675                 push    ds:stru_5B6978.LowPart ; PrivilegeValue
PAGE:0049D67B                 call    SeSinglePrivilegeCheck
PAGE:0049D680                 test    al, al
PAGE:0049D682                 jnz     loc_4AA7DB
PAGE:0049D688 loc_49D688:                        
PAGE:0049D688                 cmp     byte ptr [ebp-1Ah], 0
PAGE:0049D68C                 jnz     loc_520D52
PAGE:0049D692                 cmp     byte ptr [ebp-19h], 0
PAGE:0049D696                 jz      loc_4CCB9A
PAGE:0049D69C                 mov     [ebp-30h], esi
PAGE:0049D69F                 cmp     [ebp-28h], esi
PAGE:0049D6A2                 jnz     loc_4C1301
PAGE:0049D6A8                 lea     eax, [ebp-24h]
PAGE:0049D6AB                 push    eax
PAGE:0049D6AC                 push    dword ptr [ebp-2Ch]
PAGE:0049D6AF                 call    PsLookupProcessByProcessId
PAGE:0049D6B4 loc_49D6B4:                          


正如你看到的,这段代码拷贝给定的指针,检查是否指向用户地址空间,核对访问权限和是否有“SetDebugPrivilege”的权限,然后从

CLIENT_ID结构中找到ProcessId并传递给PsLookupProcessByProcessId函数,PsLookupProcessByProcessId的功能是得到ProcessId的EPROCESS

。函数的其余部分对我们来说没什么用,现在我们来看看PsLookupProcessByProcessId:

Code:

PAGE:0049D725                 public PsLookupProcessByProcessId
PAGE:0049D725 PsLookupProcessByProcessId proc near  
PAGE:0049D725                                      
PAGE:0049D725
PAGE:0049D725 ProcessId       = dword ptr  8
PAGE:0049D725 Process         = dword ptr  0Ch
PAGE:0049D725
PAGE:0049D725                 mov     edi, edi
PAGE:0049D727                 push    ebp
PAGE:0049D728                 mov     ebp, esp
PAGE:0049D72A                 push    ebx
PAGE:0049D72B                 push    esi
PAGE:0049D72C                 mov     eax, large fs:124h
PAGE:0049D732                 push    [ebp+ProcessId]
PAGE:0049D735                 mov     esi, eax
PAGE:0049D737                 dec     dword ptr [esi+0D4h]
PAGE:0049D73D                 push    PspCidTable
PAGE:0049D743                 call    ExMapHandleToPointer
PAGE:0049D748                 mov     ebx, eax
PAGE:0049D74A                 test    ebx, ebx
PAGE:0049D74C                 mov     [ebp+ProcessId], STATUS_INVALID_PARAMETER
PAGE:0049D753                 jz      short loc_49D787
PAGE:0049D755                 push    edi
PAGE:0049D756                 mov     edi, [ebx]
PAGE:0049D758                 cmp     byte ptr [edi], 3
PAGE:0049D75B                 jnz     short loc_49D77A
PAGE:0049D75D                 cmp     dword ptr [edi+1A4h], 0
PAGE:0049D764                 jz      short loc_49D77A
PAGE:0049D766                 mov     ecx, edi
PAGE:0049D768                 call    sub_4134A9
PAGE:0049D76D                 test    al, al
PAGE:0049D76F                 jz      short loc_49D77A
PAGE:0049D771                 mov     eax, [ebp+Process]
PAGE:0049D774                 and     [ebp+ProcessId], 0
PAGE:0049D778                 mov     [eax], edi
PAGE:0049D77A loc_49D77A:                                                                
PAGE:0049D77A                 push    ebx
PAGE:0049D77B                 push    PspCidTable
PAGE:0049D781                 call    ExUnlockHandleTableEntry
PAGE:0049D786                 pop     edi
PAGE:0049D787 loc_49D787:                          
PAGE:0049D787                 inc     dword ptr [esi+0D4h]
PAGE:0049D78D                 jnz     short loc_49D79A
PAGE:0049D78F                 lea     eax, [esi+34h]
PAGE:0049D792                 cmp     [eax], eax
PAGE:0049D794                 jnz     loc_52388A
PAGE:0049D79A loc_49D79A:                                                            
PAGE:0049D79A                 mov     eax, [ebp+ProcessId]
PAGE:0049D79D                 pop     esi
PAGE:0049D79E                 pop     ebx
PAGE:0049D79F                 pop     ebp
PAGE:0049D7A0                 retn    8


以上我们所看到的,证实了存在像HANDLE_TABLE一样组织结构的第2个进程列表。这个表叫做PspCidTable,它包括进程和线程的列表,

PsLookupProcessThreadByCid函数和PsLookupThreadByThreadId函数都用到了这个表。我们看到,句柄和句柄表的指针被传递给了

ExMapHandleToPointer函数,该函数(在句柄有效的情况下)返回一个指向描述给定句柄的表的一个元素 - HANDLE_TABLE_ENTRY。当我们用

PDBdump分析完ntoskrnl.pdb并且得到分析日志后,会得到如下结果:

Code:

struct _HANDLE_TABLE_ENTRY {
// static data ------------------------------------
// non-static data --------------------------------
 /**/ /*|0x4|*/ void* Object;
 /**/ /*|0x4|*/ unsigned long ObAttributes;
 /**/ /*|0x4|*/ struct _HANDLE_TABLE_ENTRY_INFO* InfoTable;
 /**/ /*|0x4|*/ unsigned long value;
 /**/ /*|0x4|*/ unsigned long GrantedAccess;
 /**/ /*|0x2|*/ unsigned short GrantedAccessIndex;
 /**/ /*|0x2|*/ unsigned short CreatorBackTraceIndex;
 /**/ /*|0x4|*/ long NextFreeTableEntry;
 };//


We can recover HANDLE_TABLE_ENTRY structure from this:
我们还原一下HANDLE_TABLE_ENTRY结构的C代码:

Code:

typedef struct _HANDLE_TABLE_ENTRY
{
   union
   {
       PVOID                    Object;
       ULONG                    ObAttributes;
       PHANDLE_TABLE_ENTRY_INFO InfoTable;
       ULONG                    value;
   };

   union
  {
      union
      {
         ACCESS_MASK GrantedAccess;
         struct
         {
             USHORT GrantedAccessIndex;
             USHORT CreatorBackTraceIndex;
         };
      };

      LONG NextFreeTableEntry;
  };
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;


怎么使用它呢?首先,我们比较感兴趣的是Object域的内容,它是被句柄描述的目标指针和这个表的给定元素的用法标志(我将稍后解释这句

话)。GrantedAccess域指定了通过这个句柄对目标的访问权限许可,这个很有趣。比如,以只读方式打开一个文件,修改这个域之后就可以写

这个文件了。这个方法可以用在对一些正在被读/写的文件的访问上(比如,正在被其他进程锁定的文件)。应该回到我们的问题上来了 - 通

过对PspCidTable的分析得到进程句柄列表。

要分析它我们得了解句柄表的格式,这样才能遍历这个列表。在这个地方Windows 2000和Windows XP有着巨大的不同。由于句柄表格式不尽相

同,所以我们应该把操作系统分类进行分析。

因为Windows 2000的句柄表相对简单一些,所以我们先分析它。先来看看ExMapHandleToPointer函数:

Code:

PAGE:00493285 ExMapHandleToPointer proc near        
PAGE:00493285                                      
PAGE:00493285
PAGE:00493285 HandleTable     = dword ptr  8
PAGE:00493285 Handle          = dword ptr  0Ch
PAGE:00493285
PAGE:00493285                 push    esi
PAGE:00493286                 push    [esp+Handle]
PAGE:0049328A                 push    [esp+4+HandleTable]
PAGE:0049328E                 call    ExpLookupHandleTableEntry
PAGE:00493293                 mov     esi, eax
PAGE:00493295                 test    esi, esi
PAGE:00493297                 jz      short loc_4932A9
PAGE:00493299                 push    esi
PAGE:0049329A                 push    [esp+4+HandleTable]
PAGE:0049329E                 call    ExLockHandleTableEntry
PAGE:004932A3                 neg     al
PAGE:004932A5                 sbb     eax, eax
PAGE:004932A7                 and     eax, esi
PAGE:004932A9 loc_4932A9:                          
PAGE:004932A9                 pop     esi
PAGE:004932AA                 retn    8
PAGE:004932AA ExMapHandleToPointer endp


这里我们调用搜索HANDLE_TABLE的函数ExMapHandleToPointer以及设置Lock Bit的ExLockHandleTableEntry函数。要了解句柄表的内部结构我

们必须反汇编这些函数。先从ExpLookupHandleTableEntry函数开始:

Code:

PAGE:00493545 ExpLookupHandleTableEntry proc near    
PAGE:00493545                                      
PAGE:00493545
PAGE:00493545 HandleTable     = dword ptr  0Ch
PAGE:00493545 Handle          = dword ptr  10h
PAGE:00493545
PAGE:00493545                 push    esi
PAGE:00493546                 push    edi
PAGE:00493547                 mov     edi, [esp+Handle]
PAGE:0049354B                 mov     eax, 0FFh
PAGE:00493550                 mov     ecx, edi
PAGE:00493552                 mov     edx, edi
PAGE:00493554                 mov     esi, edi
PAGE:00493556                 shr     ecx, 12h
PAGE:00493559                 shr     edx, 0Ah
PAGE:0049355C                 shr     esi, 2
PAGE:0049355F                 and     ecx, eax
PAGE:00493561                 and     edx, eax
PAGE:00493563                 and     esi, eax
PAGE:00493565                 test    edi, 0FC000000h
PAGE:0049356B                 jnz     short loc_49358A
PAGE:0049356D                 mov     eax, [esp+HandleTable]
PAGE:00493571                 mov     eax, [eax+8]
PAGE:00493574                 mov     ecx, [eax+ecx*4]
PAGE:00493577                 test    ecx, ecx
PAGE:00493579                 jz      short loc_49358A
PAGE:0049357B                 mov     ecx, [ecx+edx*4]
PAGE:0049357E                 test    ecx, ecx
PAGE:00493580                 jz      short loc_49358A
PAGE:00493582                 lea     eax, [ecx+esi*8]
PAGE:00493585 loc_493585:                            
PAGE:00493585                 pop     edi
PAGE:00493586                 pop     esi
PAGE:00493587                 retn    8
PAGE:0049358A loc_49358A:                                                      
PAGE:0049358A                 xor     eax, eax
PAGE:0049358C                 jmp     short loc_493585
PAGE:0049358C ExpLookupHandleTableEntry endp


除此之外,我们来看看从ntoskrnl.pdb中得到的HANDLE_TABLE结构:

Code:

struct _HANDLE_TABLE {
 // static data ------------------------------------
 // non-static data --------------------------------
 /**/ /*|0x4|*/ unsigned long Flags;
 /**/ /*|0x4|*/ long HandleCount;
 /**/ /*|0x4|*/ struct _HANDLE_TABLE_ENTRY*** Table;
 /**/ /*|0x4|*/ struct _EPROCESS* QuotaProcess;
 /**/ /*|0x4|*/ void* UniqueProcessId;
 /**/ /*|0x4|*/ long FirstFreeTableEntry;
 /**/ /*|0x4|*/ long NextIndexNeedingPool;
 /**/ /*|0x38|*/ struct _ERESOURCE HandleTableLock;
 /**/ /*|0x8|*/ struct _LIST_ENTRY HandleTableList;
 /**/ /*|0x10|*/ struct _KEVENT HandleContentionEvent;
}; //


根据这些数据我们用C语言还原这个结构:

Code:

typedef struct _WIN2K_HANDLE_TABLE
{
  ULONG                 Flags;
  LONG                  HandleCount;
  PHANDLE_TABLE_ENTRY **Table;
  PEPROCESS             QuotaProcess;
  HANDLE                UniqueProcessId;
  LONG                  FirstFreeTableEntry;
  LONG                  NextIndexNeedingPool;
  ERESOURCE             HandleTableLock;
  LIST_ENTRY            HandleTableList;
  KEVENT                HandleContentionEvent;
} WIN2K_HANDLE_TABLE , *PWIN2K_HANDLE_TABLE ;


显而易见,句柄表由对象表的三个层次的索引组成。现在我们再来看看ExLookhandleTableEntry函数:

Code:

PAGE:00492E2B ExLockHandleTableEntry proc near      
PAGE:00492E2B                                        
PAGE:00492E2B
PAGE:00492E2B var_8           = dword ptr -8
PAGE:00492E2B var_4           = dword ptr -4
PAGE:00492E2B HandleTable     = dword ptr  8
PAGE:00492E2B Entry           = dword ptr  0Ch
PAGE:00492E2B
PAGE:00492E2B                 push    ebp
PAGE:00492E2C                 mov     ebp, esp
PAGE:00492E2E                 push    ecx
PAGE:00492E2F                 push    ecx
PAGE:00492E30                 push    ebx
PAGE:00492E31                 push    esi
PAGE:00492E32                 xor     ebx, ebx
PAGE:00492E34 loc_492E34:                                                              
PAGE:00492E34                 mov     eax, [ebp+Entry]
PAGE:00492E37                 mov     esi, [eax]
PAGE:00492E39                 test    esi, esi
PAGE:00492E3B                 mov     [ebp+var_8], esi
PAGE:00492E3E                 jz      short loc_492E89
PAGE:00492E40                 jle     short loc_492E64
PAGE:00492E42                 mov     eax, esi
PAGE:00492E44                 or      eax, 80000000h      // set WIN2K_TABLE_ENTRY_LOCK_BIT
PAGE:00492E49                 mov     [ebp+var_4], eax
PAGE:00492E4C                 mov     eax, [ebp+var_8]
PAGE:00492E4F                 mov     ecx, [ebp+Entry]
PAGE:00492E52                 mov     edx, [ebp+var_4]
PAGE:00492E55                 cmpxchg [ecx], edx
PAGE:00492E58                 cmp     eax, esi
PAGE:00492E5A                 jnz     short loc_492E64
PAGE:00492E5C                 mov     al, 1
PAGE:00492E5E loc_492E5E:                            
PAGE:00492E5E                 pop     esi
PAGE:00492E5F                 pop     ebx
PAGE:00492E60                 leave
PAGE:00492E61                 retn    8
PAGE:00492E64 loc_492E64:              
PAGE:00492E64                 mov     eax, ebx
PAGE:00492E66                 inc     ebx
PAGE:00492E67                 cmp     eax, 1
PAGE:00492E6A                 jb      loc_4BC234
PAGE:00492E70                 mov     eax, [ebp+HandleTable]
PAGE:00492E73                 push    offset unk_46D240 ; Timeout
PAGE:00492E78                 push    0               ; Alertable
PAGE:00492E7A                 push    0               ; WaitMode
PAGE:00492E7C                 add     eax, 5Ch
PAGE:00492E7F                 push    0               ; WaitReason
PAGE:00492E81                 push    eax             ; Object
PAGE:00492E82                 call    KeWaitForSingleObject
PAGE:00492E87                 jmp     short loc_492E34
PAGE:00492E89 loc_492E89:                        
PAGE:00492E89                 xor     al, al
PAGE:00492E8B                 jmp     short loc_492E5E
PAGE:00492E8B ExLockHandleTableEntry endp


这段代码检查了HANDLE_TABLE_ENTRY结构的Object成员的第31位,设置该位,如果该位被设置,意味着等待HANDLE_TABLE的

HandleContentionEvent。对我们来说设置TABLE_ENTRY_LOCK_BIT才是最重要的,因为它是目标地址的一部分,如果标志位没有设置,我们就会

得到无效句柄。现在我们明白了句柄表的格式,可以写代码来遍历这个表了:

Code:

void ScanWin2KHandleTable(PWIN2K_HANDLE_TABLE HandleTable)
{
  int i, j, k;
  PHANDLE_TABLE_ENTRY Entry;

  for (i = 0; i < 0x100; i++)
  {
     if (HandleTable->Table[i])
     {
        for (j = 0; j < 0x100; j++)
        {
           if (HandleTable->Table[i][j])
           {
              for (k = 0; k < 0x100; k++)
              {
                 Entry = &HandleTable->Table[i][j][k];

                 if (Entry->Object)
                   ProcessObject((PVOID)((ULONG)Entry->Object | WIN2K_TABLE_ENTRY_LOCK_BIT));
              }
           }
        }
     }
  }
}


这段代码处理了所有表中的成员,并且为每一个成员调用了ProcessObject函数。ProcessObject函数检测成员类型并且恰当地处理了它们。这

个函数代码如下:

Code:

void ProcessObject(PVOID Object)
{
  POBJECT_HEADER ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);

  if (ObjectHeader->Type == *PsProcessType) CollectProcess(Object);

  if (ObjectHeader->Type == *PsThreadType)  ThreadCollect(Object);
}


我们已经了解了Windows 2000下的句柄表结构,现在开始分析Windows XP的表结构。从反汇编ExpLookupHandleTableEntry函数开始:

Code:

PAGE:0048D3C1 ExpLookupHandleTableEntry proc near  
PAGE:0048D3C1                                      
PAGE:0048D3C1
PAGE:0048D3C1 HandleTable     = dword ptr  8
PAGE:0048D3C1 Handle          = dword ptr  0Ch
PAGE:0048D3C1
PAGE:0048D3C1                 mov     edi, edi
PAGE:0048D3C3                 push    ebp
PAGE:0048D3C4                 mov     ebp, esp
PAGE:0048D3C6                 and     [ebp+Handle], 0FFFFFFFCh
PAGE:0048D3CA                 mov     eax, [ebp+Handle]
PAGE:0048D3CD                 mov     ecx, [ebp+HandleTable]
PAGE:0048D3D0                 mov     edx, [ebp+Handle]
PAGE:0048D3D3                 shr     eax, 2
PAGE:0048D3D6                 cmp     edx, [ecx+38h]
PAGE:0048D3D9                 jnb     loc_4958D6
PAGE:0048D3DF                 push    esi
PAGE:0048D3E0                 mov     esi, [ecx]
PAGE:0048D3E2                 mov     ecx, esi
PAGE:0048D3E4                 and     ecx, 3     // ecx - table level
PAGE:0048D3E7                 and     esi, not 3 // esi - pointer to first table
PAGE:0048D3EA                 sub     ecx, 0
PAGE:0048D3ED                 jnz     loc_48DEA4
PAGE:0048D3F3                 lea     eax, [esi+eax*8]
PAGE:0048D3F6 loc_48D3F6:                          
PAGE:0048D3F6                 pop     esi
PAGE:0048D3F7 loc_48D3F7:                          
PAGE:0048D3F7                 pop     ebp
PAGE:0048D3F8                 retn    8
PAGE:0048DEA4 loc_48DEA4:                          
PAGE:0048DEA4                 dec     ecx
PAGE:0048DEA5                 mov     ecx, eax
PAGE:0048DEA7                 jnz     loc_52F57A
PAGE:0048DEAD                 shr     ecx, 9
PAGE:0048DEB0                 mov     ecx, [esi+ecx*4]
PAGE:0048DEB3 loc_48DEB3:                          
PAGE:0048DEB3                 and     eax, 1FFh
PAGE:0048DEB8                 lea     eax, [ecx+eax*8]
PAGE:0048DEBB                 jmp     loc_48D3F6
PAGE:0052F57A loc_52F57A:                            
PAGE:0052F57A                 shr     ecx, 13h
PAGE:0052F57D                 mov     edx, ecx
PAGE:0052F57F                 mov     ecx, [esi+ecx*4]
PAGE:0052F582                 shl     edx, 13h
PAGE:0052F585                 sub     eax, edx
PAGE:0052F587                 mov     edx, eax
PAGE:0052F589                 shr     edx, 9
PAGE:0052F58C                 mov     ecx, [ecx+edx*4]
PAGE:0052F58F                 jmp     loc_48DEB3


再来看看ntoskrnl.pdb中的HANDLE_TABLE:

Code:

struct _HANDLE_TABLE {
// static data ------------------------------------
// non-static data --------------------------------
 /**/ /*|0x4|*/ unsigned long TableCode;
 /**/ /*|0x4|*/ struct _EPROCESS* QuotaProcess;
 /**/ /*|0x4|*/ void* UniqueProcessId;
 /**/ /*|0x10|*/ struct _EX_PUSH_LOCK HandleTableLock[4];
 /**/ /*|0x8|*/ struct _LIST_ENTRY HandleTableList;
 /**/ /*|0x4|*/ struct _EX_PUSH_LOCK HandleContentionEvent;
 /**/ /*|0x4|*/ struct _HANDLE_TRACE_DEBUG_INFO* DebugInfo;
 /**/ /*|0x4|*/ long ExtraInfoPages;
 /**/ /*|0x4|*/ unsigned long FirstFree;
 /**/ /*|0x4|*/ unsigned long LastFree;
 /**/ /*|0x4|*/ unsigned long NextHandleNeedingPool;
 /**/ /*|0x4|*/ long HandleCount;
 /**/ /*|0x4|*/ unsigned long Flags;
 /**/ /*|0x1|*/ unsigned char StrictFIFO:0:1;
 };  //


利用以上信息还原该结构:

Code:

typedef struct _XP_HANDLE_TABLE
{
  ULONG                    TableCode;
  PEPROCESS                QuotaProcess;
  PVOID                    UniqueProcessId;
  EX_PUSH_LOCK             HandleTableLock[4];
  LIST_ENTRY               HandleTableList;
  EX_PUSH_LOCK             HandleContentionEvent;
  PHANDLE_TRACE_DEBUG_INFO DebugInfo;
  LONG                     ExtraInfoPages;
  ULONG                    FirstFree;
  ULONG                    LastFree;
  ULONG                    NextHandleNeedingPool;
  LONG                     HandleCount;
  LONG                     Flags;
  UCHAR                    StrictFIFO;
} XP_HANDLE_TABLE, *PXP_HANDLE_TABLE;

从上面的表中可以很明显看出ExpLookupHandleTableEntry函数从HANDLE_TABLE结构中得到TableCode的值,并基于其低2位的内容计算出表的层

次数。其余的位指向第1层表。因此Windows XP下的HANDLE_TABLE可以拥有1到3个层次,每一个层次的表的大小为1FFh。当表中记录的数量增加

时,系统会自动增加层数。很明显,当表中记录的数量超过0x200的时候表就会拥有两层,当大于0x40000时增加到第3层。不知道当销毁对象时

系统会不会减少表的层数,我没有注意到这件事。

Windows XP下没有ExLockHandleTableEntry函数,因此表中相应的模块被定位在ExMapHandleToPointer函数中。反汇编这个函数看看它做了什

么?

Code:

PAGE:0048F61E ExMapHandleToPointer proc near        
PAGE:0048F61E                                      
PAGE:0048F61E
PAGE:0048F61E var_8           = dword ptr -8
PAGE:0048F61E var_4           = dword ptr -4
PAGE:0048F61E HandleTable     = dword ptr  8
PAGE:0048F61E Handle          = dword ptr  0Ch
PAGE:0048F61E
PAGE:0048F61E                 mov     edi, edi
PAGE:0048F620                 push    ebp
PAGE:0048F621                 mov     ebp, esp
PAGE:0048F623                 push    ecx
PAGE:0048F624                 push    ecx
PAGE:0048F625                 push    edi
PAGE:0048F626                 mov     edi, [ebp+Handle]
PAGE:0048F629                 test    di, 7FCh
PAGE:0048F62E                 jz      loc_4A2A36
PAGE:0048F634                 push    ebx
PAGE:0048F635                 push    esi
PAGE:0048F636                 push    edi
PAGE:0048F637                 push    [ebp+HandleTable]
PAGE:0048F63A                 call    ExpLookupHandleTableEntry
PAGE:0048F63F                 mov     esi, eax
PAGE:0048F641                 test    esi, esi
PAGE:0048F643                 jz      loc_4A2711
PAGE:0048F649                 mov     [ebp+var_4], esi
PAGE:0048F64C loc_48F64C:                                                            
PAGE:0048F64C                 mov     ebx, [esi]
PAGE:0048F64E                 test    bl, 1
PAGE:0048F651                 mov     [ebp+var_8], ebx
PAGE:0048F654                 jz      loc_508844
PAGE:0048F65A                 lea     eax, [ebx-1]
PAGE:0048F65D                 mov     [ebp+Handle], eax
PAGE:0048F660                 mov     eax, [ebp+var_8]
PAGE:0048F663                 mov     ecx, [ebp+var_4]
PAGE:0048F666                 mov     edx, [ebp+Handle]
PAGE:0048F669                 cmpxchg [ecx], edx
PAGE:0048F66C                 cmp     eax, ebx
PAGE:0048F66E                 jnz     loc_50884C
PAGE:0048F674                 mov     eax, esi
PAGE:0048F676 loc_48F676:                          
PAGE:0048F676                 pop     esi
PAGE:0048F677                 pop     ebx
PAGE:0048F678 loc_48F678:                          
PAGE:0048F678                 pop     edi
PAGE:0048F679                 leave
PAGE:0048F67A                 retn    8
PAGE:0048F67A ExMapHandleToPointer endp


ExpLookuphandleTableEntry函数返回指向HANDLE_TABLE_ENTRY的指针后,我们要检查Object域的低字节,如果是被设置了的,说明是被清除了

的(俄文翻译者kao注:希望我翻译的对...),如果该位没有被设置,我们要等到它被设置了为止。因此当得到对象地址的时候我们不应该设

置高位(Windows 2000平台),而是要清除低位。综上所述,扫描表的代码如下:

Code:

void ScanXpHandleTable(PXP_HANDLE_TABLE HandleTable)
{
  int i, j, k;
  PHANDLE_TABLE_ENTRY Entry;
  ULONG TableCode = HandleTable->TableCode & ~TABLE_LEVEL_MASK;

  switch (HandleTable->TableCode & TABLE_LEVEL_MASK)
  {
     case 0 :
       for (i = 0; i < 0x200; i++)
       {
          Entry = &((PHANDLE_TABLE_ENTRY)TableCode)[i];

          if (Entry->Object) ProcessObject((PVOID)((ULONG)Entry->Object & ~XP_TABLE_ENTRY_LOCK_BIT));
       }         
     break;

     case 1 :
       for (i = 0; i < 0x200; i++)
       {
          if (((PVOID *)TableCode)[i])
          {
             for (j = 0; j < 0x200; j++)
             {
                Entry = &((PHANDLE_TABLE_ENTRY *)TableCode)[i][j];

                if (Entry->Object) ProcessObject((PVOID)((ULONG)Entry->Object & ~XP_TABLE_ENTRY_LOCK_BIT));
             }
          }
       }      
     break;

     case 2 :
       for (i = 0; i < 0x200; i++)
       {
          if (((PVOID *)TableCode)[i])
          {
             for (j = 0; j < 0x200; j++)
             {
                if (((PVOID **)TableCode)[i][j])
                {
                   for (k = 0; k < 0x200; k++)
                   {
                      Entry = &((PHANDLE_TABLE_ENTRY **)TableCode)[i][j][k];

                      if (Entry->Object)
                        ProcessObject((PVOID)((ULONG)Entry->Object & ~XP_TABLE_ENTRY_LOCK_BIT));
                   }
                }
             }
          }
       }     
     break;
  }
}


我们已经明白对象表格式了。现在想要枚举进程我们还需要得到PspCidTable的地址。也许你已经猜到了,我们要在

PsLookupProcessByProcessId函数中搜索,这个函数中的第1个函数调用包含着PspCidTable的地址。代码如下:

Code:

void GetPspCidTable()
{
  PUCHAR cPtr, pOpcode;
  ULONG Length;

  for (cPtr = (PUCHAR)PsLookupProcessByProcessId;
       cPtr < (PUCHAR)PsLookupProcessByProcessId + PAGE_SIZE;
            cPtr += Length)
  {
     Length = SizeOfCode(cPtr, &pOpcode);

     if (!Length) break;

     if (*(PUSHORT)cPtr == 0x35FF && *(pOpcode + 6) == 0xE8)
     {
        PspCidTable = **(PVOID **)(pOpcode + 2);
        break;
     }
  }
}

现在我们知道怎样处理PspCidTable了,可以非常容易地查看到所有进程的表中的所有元素,分析那些属于隐藏进程的对象,就像我们在用户态做的一样,如果你已经理解了前面所讲的东西,你一定可以做得到。

视觉特征-自监督-同源不同crop视图一致性【2025-08】:DINO-v3附录 Appendix 附录A 大规模训练中的伪影与异常值本节探讨了最近在 LLM 领域(An 等人,2025 年)以及视觉领域(Darcet 等人,2024 年)的大模型训练过程中所出现的异常值与特殊现象。借鉴 An 等人(2025 年)的定义,异常值通常指的是那些其数值与所在分布的平均值存在显著偏差的神经网络激活值。在 DINOv3 的训练过程中,我们发现了不同层面的此类异常值:有些出现在图像块层面,而另些则出现在特征维度层面。下面我们将介绍所观察到的各类异常值,以及它们对训练过程和最终结果的影响。 阅读详情

相关推荐

视觉特征-自监督-掩码像素预测【2021-11】:MAE【随机遮挡75%块】【编码器仅处理可见块+轻量解码器重建像素】【以MSE计算重建结果与原图差异】【高掩码率提效】

本文表明,掩码自编码器(MAE)是适用于计算机视觉的可扩展自监督学习器。我们的 MAE 方法很简单:我们对输入图像的随机图像块进行掩码,并重建缺失的像素。它基于两项核心设计。首先,我们开发了一种非对称编码器-解码器架构,其中编码器仅处理可见的图像块子集(不使用掩码 Tokens),同时配备一个轻量级解码器,根据潜在表示和掩码 Tokens 重建原始图像。其次,我们发现,对输入图像进行高比例掩码,例如 75%,能够产生一项非平凡且有意义的自监督任务。

u013250861的博客 163

Thread Local Storage

Source: http://www.nynaeve.net/?p=180 ThreadLocal Storage, part 1: Overview Windows, likepractically any other mainstream multithreading operating system, provides amechanism to allow programmers to

Bill的专栏 2519

视觉特征-自监督-同源不同crop视图一致性【2021-04】:DINOv1【全局裁剪(覆盖原图较大区域)➜教师;全局&局部(覆盖原图较小区域)裁剪➜学生】【EMA学生来构建教师】【中心化&锐化】

在本文中,我们探讨 自 监督学习是否为 Vision Transformer (ViT) [19] 提供了区别于卷积 网 络(convnets)的新特性。除了将自监督方法适配到该架构后效果尤为出色这一事实之外,我们还提出以下观察:首先,自监督ViT特征包含有关图像语义分割的显式信息,而这种信息在有监督ViT和convnets中均不会如此清晰地出现。其次,这些特征也是出色的 k-NN 分类器,使用小型 ViT 在 ImageNet 上达到78.3%的top-1准确率。

u013250861的博客 300

线程局部存储(TLS)

线程局部存储,Part 1:概述 线程局部存储,Part 2:显式TLS 线程局部存储,Part 3:编译器和链接器对隐式TLS的支持 线程局部存储,Part 4:访问__declspec(thread)变量 线程局部存储,Part 5:加载器对__declspec(thread)变量的支持(进程初始化阶段) 线程局部存储,Part 6:Windows Server...

weixin_34409703的博客 546

RAG中的Chunking实战:语义单元封装与15种工程化切分技术

在检索增强生成(RAG)系统中,chunking并非简单的文本分段,而是面向语义完整性、上下文锚定与检索友好性的关键预处理环节。其核心原理在于将原始文档转化为具备命题结构、可追溯元信息和向量表征鲁棒性的语义单元。技术价值体现在显著提升recall@5与QA准确率,避免因切分失当导致的关键条件断裂、术语割裂或逻辑链丢失。典型应用场景覆盖法律条款解析、技术文档问答、医疗指南检索及多模态手册理解等。本文聚焦RAG落地中最易被低估却影响最大的chunking工程实践,系统梳理15种可组合、可嵌套的切分策略,并深度结

com_xpp的专栏 523

恶意代码检测技术对比:4种主流方案(特征扫描、沙箱、蜜罐、AI)实战解析

本文深入对比了四种主流恶意代码检测技术:特征扫描、沙箱、蜜罐和AI模型,详细解析了各自的技术原理、实战配置及优缺点。通过Python代码示例和性能测试数据,为网络安全从业者提供了构建多层次防御体系的实用方案,特别针对金融等行业的高安全需求场景给出了组合策略建议。

cojm55771的博客 422

侦测隐藏进程的强文

俄语原文:http://wasm.ru/article.php?article=hiddndt俄文翻译:kaohttp://community.reverse-engineering.net/viewtopic.php?t=4685中文翻译: prince后期校验:firstrose=============================侦测隐藏进程[C] Ms-Rem2002-2005 was

6272

【信息科学与工程学】【通信工程】第七十六篇 网络解决方案设计03

继续围绕智算中心相关的组网设计、光路设计、波分设计、OTN设备、交换机设备、组网拓扑、网络操作系统、业务区/管理区/存储区、光设计、租户设计、电磁设计、队列设计、QoS设计、调度设计、组合设计等领域。: 若Tpxe​=2min,Tos_install​=5min,Tagent​=0.5min,Tconfig​=0.5min,则Tready​=8min。: 若N=2000,Ms​=2,Nstorage​=128,Mss​=2,则Nip​=2000×2+128×2=4256,需要/20子网。

weixin_49199313的博客 204

Rebuttal to the KCC v1.0 Code Audit(Rebuttal Page No.2)

The auditor claims that KCC’s AND-gate fluid model assumes a “vacuum” (no cross-traffic) and that the DRAIN timeout “推翻严格最优性证明” (overturns the strict optimality proof). Both claims are incorrect.

liulilittle的博客 626

Ansys Zemax导入CAD文件避坑指南:STL/STEP/IGES格式选择与性能优化

本文深度解析了在Ansys Zemax中导入CAD文件的核心要点与性能优化策略。针对STL、STEP、IGES等主流格式,详细对比了其几何表示、精度与适用场景差异,并提供了基于模型特征的格式选择决策树。文章重点分享了体元、弦公差等高级参数的调优技巧,以及处理复杂装配体和避免精度陷阱的实战指南,旨在帮助光学工程师提升仿真效率与结果可靠性。

weixin_29202687的博客 436

【电磁场与电磁波课程设计】基于HFSS的双频微带天线仿真及设计

第五代移动通信技术(5G)的商用已经近在眼前,作为移动通信中最为重要的功能组件,面向5G通信应用的手机天线与基站天线的技术研究与设计成为近年来的热点。随着移动通信中“跳频”“扩频”等通信技术发展,为了满足与多个终端的通信要求,实现多系统共用和收发共用等功能,这就要求天线在不同频段下工作。根据国家工信部的最新规定,5G移动通信的中频段被划定在3.3 GHz-3.5 GHz及4.8 GHz-5.0 GH,面向5G移动通信的天线应具有高增益、小型化、宽频段及高隔离度等技术特征,以满足5G通信的高传输速率、波束智能

weixin_44450575的博客 1万+

CANN仿真器AIC性能问题指南

AIC-side pipeline bound diagnosis and fix solutions. All metric paths refer to fields in `summary.json`. > **Reading order**: scan the Quick Reference Table for a row matching your symptoms, then jum

gitblog_00464的博客 803

深入RK3588内存管理:如何用dma_buf_alloc和mpp_buffer为RGA2分配4G以内内存(附kernel版本选择)

本文深入探讨了RK3588平台上RGA2核心的4G内存限制问题,详细解析了dma_buf_alloc和mpp_buffer两种内存分配方案的技术细节与适用场景。通过代码示例和性能对比,帮助开发者优化高分辨率视频处理流程,并提供了内核版本选择与常见错误排查指南。

weixin_29168153的博客 364

开源软件发展史 | 演进及开源许可证

……

u013669912的博客 2621

H3C模拟器HCL防火墙路由模式主备配置实验

摘要:本文详细介绍了网络拓扑规划与设备配置方案。运营商仅提供一个接口地址,通过虚IP技术实现双防火墙高可用部署:主备防火墙分别配置10.20.10.1/29和10.20.10.2/29作为实际上联地址,共享10.10.10.2/24虚IP;下联配置172.168.10.0/24网段,核心交换机配置VLAN接口及192.168.10.1、192.168.20.1两个下联接口。文章包含运营商路由器、防火墙(HA配置、VVRP设置)和核心交换机的具体命令行配置步骤,并强调需进行主备切换验证。整个方案实现了单运营商

LQ的博客 1122

使用verilog设计实现QR分解

QR(正交三角)分解法是求一般矩阵全部特征值的最有效并广泛应用的方法,一般矩阵先经过正交相似变化成为Hessenberg矩阵,然后再应用QR方法求特征值和特征向量。 使用verilog对QR实现方法进行设计并进行功能仿真: 如下: 顶层模块代码: module TOP( H1R, H1I, H2R, H2I, H3R, H3I, H4R, H4I, clk, rst_n, Y1R, Y1I, Y2R, Y2I, X1R, X1I, X2R, X2I ); parameter width = 16; // I

QQ_778132974的博客 2150

Unity Android log view(Android Debug Bridge)

Android Debug Bridge

蛰伏--当你不够强大时,就不要放弃努力 4156

生产环境中redis的SCAN命令如何替代KEYS命令?

本文介绍了使用SCAN命令替代KEYS的完整方案。SCAN通过游标分批迭代,避免KEYS命令遍历整个键空间导致的阻塞风险。文章详细说明了SCAN的基础语法、迭代流程和Java实现示例,强调必须关闭Cursor避免连接泄漏。针对生产环境,提供了COUNT参数调优、客户端去重、集群模式适配等优化建议,并列举了典型错误场景。SCAN通过分批次迭代和游标管理,可安全实现高性能键空间遍历。全文150字。

DN金猿的博客 179

Rage(PC) Modding Notes : Console Commands

_________________________________________ ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Rage(PC) Modding Notes : Console

tailiangliang的专栏 2341

量化 insider 爆料! 散户打板必亏?(打板必看!量化资金如何“隔日砸盘”收割散户)

量化开发 insider 爆料💥 散户追的涨停板📉,可能是机构故意做的陷阱!本文从技术视角拆解“涨停陷阱”,帮你看清市场真实逻辑🧠。

XiaoqiangClub的博客 450

DeepSeek + 微信:手把手教你把 AI 接入个人微信,秒变“高情商”聊天助手(防封指南)

将 DeepSeek 接入微信,本质上是给你的微信装了一个**“最强外挂”**。它不仅能帮你回消息,还能帮你写周报、查资料、甚至陪聊解闷。工具是服务于人的,不要让 AI 完全替代了你的真情实感。毕竟,真正的高情商,是屏幕背后那个在乎对方的你。先申请一个小号,按照步骤跑通流程。然后试着调整 Prompt,训练一个属于你自己的“嘴替”吧!

BUG猿的博客 957

机器人-模仿学习-双臂【2023-04】:ACT【80M参数;CVAE-Transformer架构;ResNet18编码四路视觉;4层编码器融合视觉状态;7层解码器并行预测100步动作;时序集成平滑】

Meta。

u013250861的博客 428

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

ViT:An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale

尽管 Transformer 架构已成为自然语言处理任务事实上的标准,但其在计算机视觉中的应用仍然有限。在视觉领域,注意力机制要么与卷积网络结合应用,要么用于替代卷积网络的某些组件,同时保留其整体结构。我们表明,这种对 CNN 的依赖并非必要,直接应用于图像块序列的纯 Transformer 在图像分类任务上也能表现出色。

u013250861的博客 79

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

上一篇: DSHOW 中关于FILTER的连接内部实现过程
下一篇: 关于进程间的访问权限等进程间控制资料收集
tomorrowsprogress
博客等级 码龄20年 16粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值