watermelon 发表于 2020-2-5 15:01:09

【C语言】利用PEB和LDR遍历指定进程的模块

本程序理解上收到了群MinL同学的指导,非常感谢!

内核中PEB块和LDR链用处非常多,常见的用处比如就是本贴要讨论的遍历制定进程的模块;

实验环境:Visual Studio 2013,WDK8.1,Windows 7 x64虚拟机,Windbg,DbgView,KMDmanager。

首先我们用Windbg看一下EPROCESS,PEB在EPROCESS中是以PPEB指针形式作为成员变量的:
EPROCESS结构体:
kd> dt _EPROCESS
nt!_EPROCESS
   +0x000 Pcb            : _KPROCESS
   +0x160 ProcessLock      : _EX_PUSH_LOCK
   +0x168 CreateTime       : _LARGE_INTEGER
   +0x170 ExitTime         : _LARGE_INTEGER
   +0x178 RundownProtect   : _EX_RUNDOWN_REF
   +0x180 UniqueProcessId: Ptr64 Void
   +0x188 ActiveProcessLinks : _LIST_ENTRY
   +0x198 ProcessQuotaUsage : Uint8B
   +0x1a8 ProcessQuotaPeak : Uint8B
   +0x1b8 CommitCharge   : Uint8B
   +0x1c0 QuotaBlock       : Ptr64 _EPROCESS_QUOTA_BLOCK
   +0x1c8 CpuQuotaBlock    : Ptr64 _PS_CPU_QUOTA_BLOCK
   +0x1d0 PeakVirtualSize: Uint8B
   +0x1d8 VirtualSize      : Uint8B
   +0x1e0 SessionProcessLinks : _LIST_ENTRY
   +0x1f0 DebugPort      : Ptr64 Void
   +0x1f8 ExceptionPortData : Ptr64 Void
   +0x1f8 ExceptionPortValue : Uint8B
   +0x1f8 ExceptionPortState : Pos 0, 3 Bits
   +0x200 ObjectTable      : Ptr64 _HANDLE_TABLE
   +0x208 Token            : _EX_FAST_REF
   +0x210 WorkingSetPage   : Uint8B
   +0x218 AddressCreationLock : _EX_PUSH_LOCK
   +0x220 RotateInProgress : Ptr64 _ETHREAD
   +0x228 ForkInProgress   : Ptr64 _ETHREAD
   +0x230 HardwareTrigger: Uint8B
   +0x238 PhysicalVadRoot: Ptr64 _MM_AVL_TABLE
   +0x240 CloneRoot      : Ptr64 Void
   +0x248 NumberOfPrivatePages : Uint8B
   +0x250 NumberOfLockedPages : Uint8B
   +0x258 Win32Process   : Ptr64 Void
   +0x260 Job            : Ptr64 _EJOB
   +0x268 SectionObject    : Ptr64 Void
   +0x270 SectionBaseAddress : Ptr64 Void
   +0x278 Cookie         : Uint4B
   +0x27c UmsScheduledThreads : Uint4B
   +0x280 WorkingSetWatch: Ptr64 _PAGEFAULT_HISTORY
   +0x288 Win32WindowStation : Ptr64 Void
   +0x290 InheritedFromUniqueProcessId : Ptr64 Void
   +0x298 LdtInformation   : Ptr64 Void
   +0x2a0 Spare            : Ptr64 Void
   +0x2a8 ConsoleHostProcess : Uint8B
   +0x2b0 DeviceMap      : Ptr64 Void
   +0x2b8 EtwDataSource    : Ptr64 Void
   +0x2c0 FreeTebHint      : Ptr64 Void
   +0x2c8 FreeUmsTebHint   : Ptr64 Void
   +0x2d0 PageDirectoryPte : _HARDWARE_PTE
   +0x2d0 Filler         : Uint8B
   +0x2d8 Session          : Ptr64 Void
   +0x2e0 ImageFileName    : UChar
   +0x2ef PriorityClass    : UChar
   +0x2f0 JobLinks         : _LIST_ENTRY
   +0x300 LockedPagesList: Ptr64 Void
   +0x308 ThreadListHead   : _LIST_ENTRY
   +0x318 SecurityPort   : Ptr64 Void
   +0x320 Wow64Process   : Ptr64 Void
   +0x328 ActiveThreads    : Uint4B
   +0x32c ImagePathHash    : Uint4B
   +0x330 DefaultHardErrorProcessing : Uint4B
   +0x334 LastThreadExitStatus : Int4B
   +0x338 Peb            : Ptr64 _PEB
   +0x340 PrefetchTrace    : _EX_FAST_REF
   +0x348 ReadOperationCount : _LARGE_INTEGER
   +0x350 WriteOperationCount : _LARGE_INTEGER
   +0x358 OtherOperationCount : _LARGE_INTEGER
   +0x360 ReadTransferCount : _LARGE_INTEGER
   +0x368 WriteTransferCount : _LARGE_INTEGER
   +0x370 OtherTransferCount : _LARGE_INTEGER
   +0x378 CommitChargeLimit : Uint8B
   +0x380 CommitChargePeak : Uint8B
   +0x388 AweInfo          : Ptr64 Void
   +0x390 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
   +0x398 Vm               : _MMSUPPORT
   +0x420 MmProcessLinks   : _LIST_ENTRY
   +0x430 HighestUserAddress : Ptr64 Void
   +0x438 ModifiedPageCount : Uint4B
   +0x43c Flags2         : Uint4B
   +0x43c JobNotReallyActive : Pos 0, 1 Bit
   +0x43c AccountingFolded : Pos 1, 1 Bit
   +0x43c NewProcessReported : Pos 2, 1 Bit
   +0x43c ExitProcessReported : Pos 3, 1 Bit
   +0x43c ReportCommitChanges : Pos 4, 1 Bit
   +0x43c LastReportMemory : Pos 5, 1 Bit
   +0x43c ReportPhysicalPageChanges : Pos 6, 1 Bit
   +0x43c HandleTableRundown : Pos 7, 1 Bit
   +0x43c NeedsHandleRundown : Pos 8, 1 Bit
   +0x43c RefTraceEnabled: Pos 9, 1 Bit
   +0x43c NumaAware      : Pos 10, 1 Bit
   +0x43c ProtectedProcess : Pos 11, 1 Bit
   +0x43c DefaultPagePriority : Pos 12, 3 Bits
   +0x43c PrimaryTokenFrozen : Pos 15, 1 Bit
   +0x43c ProcessVerifierTarget : Pos 16, 1 Bit
   +0x43c StackRandomizationDisabled : Pos 17, 1 Bit
   +0x43c AffinityPermanent : Pos 18, 1 Bit
   +0x43c AffinityUpdateEnable : Pos 19, 1 Bit
   +0x43c PropagateNode    : Pos 20, 1 Bit
   +0x43c ExplicitAffinity : Pos 21, 1 Bit
   +0x440 Flags            : Uint4B
   +0x440 CreateReported   : Pos 0, 1 Bit
   +0x440 NoDebugInherit   : Pos 1, 1 Bit
   +0x440 ProcessExiting   : Pos 2, 1 Bit
   +0x440 ProcessDelete    : Pos 3, 1 Bit
   +0x440 Wow64SplitPages: Pos 4, 1 Bit
   +0x440 VmDeleted      : Pos 5, 1 Bit
   +0x440 OutswapEnabled   : Pos 6, 1 Bit
   +0x440 Outswapped       : Pos 7, 1 Bit
   +0x440 ForkFailed       : Pos 8, 1 Bit
   +0x440 Wow64VaSpace4Gb: Pos 9, 1 Bit
   +0x440 AddressSpaceInitialized : Pos 10, 2 Bits
   +0x440 SetTimerResolution : Pos 12, 1 Bit
   +0x440 BreakOnTermination : Pos 13, 1 Bit
   +0x440 DeprioritizeViews : Pos 14, 1 Bit
   +0x440 WriteWatch       : Pos 15, 1 Bit
   +0x440 ProcessInSession : Pos 16, 1 Bit
   +0x440 OverrideAddressSpace : Pos 17, 1 Bit
   +0x440 HasAddressSpace: Pos 18, 1 Bit
   +0x440 LaunchPrefetched : Pos 19, 1 Bit
   +0x440 InjectInpageErrors : Pos 20, 1 Bit
   +0x440 VmTopDown      : Pos 21, 1 Bit
   +0x440 ImageNotifyDone: Pos 22, 1 Bit
   +0x440 PdeUpdateNeeded: Pos 23, 1 Bit
   +0x440 VdmAllowed       : Pos 24, 1 Bit
   +0x440 CrossSessionCreate : Pos 25, 1 Bit
   +0x440 ProcessInserted: Pos 26, 1 Bit
   +0x440 DefaultIoPriority : Pos 27, 3 Bits
   +0x440 ProcessSelfDelete : Pos 30, 1 Bit
   +0x440 SetTimerResolutionLink : Pos 31, 1 Bit
   +0x444 ExitStatus       : Int4B
   +0x448 VadRoot          : _MM_AVL_TABLE
   +0x488 AlpcContext      : _ALPC_PROCESS_CONTEXT
   +0x4a8 TimerResolutionLink : _LIST_ENTRY
   +0x4b8 RequestedTimerResolution : Uint4B
   +0x4bc ActiveThreadsHighWatermark : Uint4B
   +0x4c0 SmallestTimerResolution : Uint4B
   +0x4c8 TimerResolutionStackRecord : Ptr64 _PO_DIAG_STACK_RECORD

可以看到在x64中EPROCESS在0x338出的偏移(offset)是成员Peb(64位_PEB结构体指针),利用指针的偏移量即可获得PEB结构体指针的地址;

同样看一下在PEB结构体中:
kd> dt _PEB
nt!_PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 BitField         : UChar
   +0x003 ImageUsesLargePages : Pos 0, 1 Bit
   +0x003 IsProtectedProcess : Pos 1, 1 Bit
   +0x003 IsLegacyProcess: Pos 2, 1 Bit
   +0x003 IsImageDynamicallyRelocated : Pos 3, 1 Bit
   +0x003 SkipPatchingUser32Forwarders : Pos 4, 1 Bit
   +0x003 SpareBits      : Pos 5, 3 Bits
   +0x008 Mutant         : Ptr64 Void
   +0x010 ImageBaseAddress : Ptr64 Void
   +0x018 Ldr            : Ptr64 _PEB_LDR_DATA
   +0x020 ProcessParameters : Ptr64 _RTL_USER_PROCESS_PARAMETERS
   +0x028 SubSystemData    : Ptr64 Void
   +0x030 ProcessHeap      : Ptr64 Void
   +0x038 FastPebLock      : Ptr64 _RTL_CRITICAL_SECTION
   +0x040 AtlThunkSListPtr : Ptr64 Void
   +0x048 IFEOKey          : Ptr64 Void
   +0x050 CrossProcessFlags : Uint4B
   +0x050 ProcessInJob   : Pos 0, 1 Bit
   +0x050 ProcessInitializing : Pos 1, 1 Bit
   +0x050 ProcessUsingVEH: Pos 2, 1 Bit
   +0x050 ProcessUsingVCH: Pos 3, 1 Bit
   +0x050 ProcessUsingFTH: Pos 4, 1 Bit
   +0x050 ReservedBits0    : Pos 5, 27 Bits
   +0x058 KernelCallbackTable : Ptr64 Void
   +0x058 UserSharedInfoPtr : Ptr64 Void
   +0x060 SystemReserved   : Uint4B
   +0x064 AtlThunkSListPtr32 : Uint4B
   +0x068 ApiSetMap      : Ptr64 Void
   +0x070 TlsExpansionCounter : Uint4B
   +0x078 TlsBitmap      : Ptr64 Void
   +0x080 TlsBitmapBits    : Uint4B
   +0x088 ReadOnlySharedMemoryBase : Ptr64 Void
   +0x090 HotpatchInformation : Ptr64 Void
   +0x098 ReadOnlyStaticServerData : Ptr64 Ptr64 Void
   +0x0a0 AnsiCodePageData : Ptr64 Void
   +0x0a8 OemCodePageData: Ptr64 Void
   +0x0b0 UnicodeCaseTableData : Ptr64 Void
   +0x0b8 NumberOfProcessors : Uint4B
   +0x0bc NtGlobalFlag   : Uint4B
   +0x0c0 CriticalSectionTimeout : _LARGE_INTEGER
   +0x0c8 HeapSegmentReserve : Uint8B
   +0x0d0 HeapSegmentCommit : Uint8B
   +0x0d8 HeapDeCommitTotalFreeThreshold : Uint8B
   +0x0e0 HeapDeCommitFreeBlockThreshold : Uint8B
   +0x0e8 NumberOfHeaps    : Uint4B
   +0x0ec MaximumNumberOfHeaps : Uint4B
   +0x0f0 ProcessHeaps   : Ptr64 Ptr64 Void
   +0x0f8 GdiSharedHandleTable : Ptr64 Void
   +0x100 ProcessStarterHelper : Ptr64 Void
   +0x108 GdiDCAttributeList : Uint4B
   +0x110 LoaderLock       : Ptr64 _RTL_CRITICAL_SECTION
   +0x118 OSMajorVersion   : Uint4B
   +0x11c OSMinorVersion   : Uint4B
   +0x120 OSBuildNumber    : Uint2B
   +0x122 OSCSDVersion   : Uint2B
   +0x124 OSPlatformId   : Uint4B
   +0x128 ImageSubsystem   : Uint4B
   +0x12c ImageSubsystemMajorVersion : Uint4B
   +0x130 ImageSubsystemMinorVersion : Uint4B
   +0x138 ActiveProcessAffinityMask : Uint8B
   +0x140 GdiHandleBuffer: Uint4B
   +0x230 PostProcessInitRoutine : Ptr64   void
   +0x238 TlsExpansionBitmap : Ptr64 Void
   +0x240 TlsExpansionBitmapBits : Uint4B
   +0x2c0 SessionId      : Uint4B
   +0x2c8 AppCompatFlags   : _ULARGE_INTEGER
   +0x2d0 AppCompatFlagsUser : _ULARGE_INTEGER
   +0x2d8 pShimData      : Ptr64 Void
   +0x2e0 AppCompatInfo    : Ptr64 Void
   +0x2e8 CSDVersion       : _UNICODE_STRING
   +0x2f8 ActivationContextData : Ptr64 _ACTIVATION_CONTEXT_DATA
   +0x300 ProcessAssemblyStorageMap : Ptr64 _ASSEMBLY_STORAGE_MAP
   +0x308 SystemDefaultActivationContextData : Ptr64 _ACTIVATION_CONTEXT_DATA
   +0x310 SystemAssemblyStorageMap : Ptr64 _ASSEMBLY_STORAGE_MAP
   +0x318 MinimumStackCommit : Uint8B
   +0x320 FlsCallback      : Ptr64 _FLS_CALLBACK_INFO
   +0x328 FlsListHead      : _LIST_ENTRY
   +0x338 FlsBitmap      : Ptr64 Void
   +0x340 FlsBitmapBits    : Uint4B
   +0x350 FlsHighIndex   : Uint4B
   +0x358 WerRegistrationData : Ptr64 Void
   +0x360 WerShipAssertPtr : Ptr64 Void
   +0x368 pContextData   : Ptr64 Void
   +0x370 pImageHeaderHash : Ptr64 Void
   +0x378 TracingFlags   : Uint4B
   +0x378 HeapTracingEnabled : Pos 0, 1 Bit
   +0x378 CritSecTracingEnabled : Pos 1, 1 Bit
   +0x378 SpareTracingBits : Pos 2, 30 Bits
可以看出,PEB结构体地址在0x018处偏移是指向了Ldr(_PEB_LDR_DATA结构体的64位指针),同样利用指针的偏移可以得到它,进一步看看_PEB_LDR_DATA这个结构图中有什么:

_PEB_LDR_DATA:
kd> dt _PEB_LDR_DATA
nt!_PEB_LDR_DATA
   +0x000 Length         : Uint4B
   +0x004 Initialized      : UChar
   +0x008 SsHandle         : Ptr64 Void
   +0x010 InLoadOrderModuleList : _LIST_ENTRY
   +0x020 InMemoryOrderModuleList : _LIST_ENTRY
   +0x030 InInitializationOrderModuleList : _LIST_ENTRY
   +0x040 EntryInProgress: Ptr64 Void
   +0x048 ShutdownInProgress : UChar
   +0x050 ShutdownThreadId : Ptr64 Void

PEB_LDR_DATA中,在0x010偏移处可以得到InLoadOrderModuleList这个链表,是双向链表_LIST_ENTRY类型的,我们最后遍历这个LIST_ENTRY链表就可以了。

以下为代码实现,先说一下几个注意事项:

1.程序中需要利用PsLookupProcessByProcessId来获取制定进程的PERPROCESS,然后进行指针的偏移量计算来获得PEB, LDR以及最后的双向链表InLoadOrderModuleList,
其中比如我们第一次得到了PEPROCESS,将其转化为ULONG64类型的数,所做的(ULONG64)eproc + 0x338得到的是PEB结构体指针的地址,
所以我们如果要得到PEB结构体的指针(也就是说PEB结构体的地址)的话,需要再用“*”来取他的值,也就是这样:ULONG64 peb_addr = *(PULONG64)((ULONG64)eproc + 0x338);同理获得ldr结构体的地址也需要注意!

2.获取PEB结构体的地址可以使用API:PsGetProcessPeb,但是为了锻炼C语言指针的应用能力,本程序全部使用结构体指针的偏移来获取。

3.本程序用枚举当前虚拟机Win7 x64上的explorer.exe的模块为例,如果重复使用的话需要重新更新一下所要枚举的进程的当前句柄。

程序如下(C语言):
#include <ntddk.h>
#include <windef.h>

#define PEB_OFFSET_IN_EPROCESS 0x338
#define LDR_OFFSET_IN_PEB 0x018
#define InLoadOrderModuleList_OFFSET 0x010


typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY64    InLoadOrderLinks;
    LIST_ENTRY64    InMemoryOrderLinks;
    LIST_ENTRY64    InInitializationOrderLinks;
    PVOID            DllBase;
    PVOID            EntryPoint;
    ULONG            SizeOfImage;
    UNICODE_STRING    FullDllName;
    UNICODE_STRING   BaseDllName;
    ULONG            Flags;
    USHORT            LoadCount;
    USHORT            TlsIndex;
    PVOID            SectionPointer;
    ULONG            CheckSum;
    PVOID            LoadedImports;
    PVOID            EntryPointActivationContext;
    PVOID            PatchInformation;
    LIST_ENTRY64    ForwarderLinks;
    LIST_ENTRY64    ServiceTagLinks;
    LIST_ENTRY64    StaticLinks;
    PVOID            ContextInformation;
    ULONG64            OriginalBase;
    LARGE_INTEGER    LoadTime;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
typedef struct _KAPC_STATE
{
    LIST_ENTRY ApcListHead;
    PKPROCESS Process;
    UCHAR KernelApcInProgress;
    UCHAR KernelApcPending;
    UCHAR UserApcPending;
} KAPC_STATE, *PKAPC_STATE;

// Functions declaration
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
VOID KeStackAttachProcess(PRKPROCESS PROCESS,PKAPC_STATE ApcState);
VOID KeUnstackDetachProcess(PKAPC_STATE ApcState);


//
// Get the pointer to the EPROCESS of the specific pid on the process.
//
PEPROCESS LookupProcess(HANDLE hPid)
{
    PEPROCESS eproc = NULL;
    if (NT_SUCCESS(PsLookupProcessByProcessId(hPid, &eproc)))
    {
      return eproc;
    }
    return NULL;
}



// Enumerate the module of the specific process.
VOID EnumerateModule(HANDLE hPid)
{
    KAPC_STATE ks;
    PEPROCESS eproc = LookupProcess(hPid);
    if (eproc == NULL)
    {
      DbgPrint("Can't find the EPROCESS...\n");
      return;
    }


    __try
    {
      // Get the peb address, the PEB structure in the EPROCESS is a pointer to PEB, named PPEB.
      // So the "(ULONG64)eproc + PEB_OFFSET_IN_EPROCESS" is the address the pointer to PEB.
      // And finally, use the "*" to get the address to the PEB structure.
      ULONG64 peb = *(PULONG64)((ULONG64)eproc + PEB_OFFSET_IN_EPROCESS);

      KeStackAttachProcess(eproc, &ks);


      // The LDR structure in PEB is also a pointer to PEB_LDR_DATA
      // So, "(ULONG64)peb + LDR_OFFSET_IN_PEB" is the address of pointer to PEB_LDR_DATA.
      // And the ULONG64 ldr is finally get the address of PEB_LDR_DATA structure.
      ULONG64 ldr = *(PULONG64)((ULONG64)peb + LDR_OFFSET_IN_PEB);

      // Get the address of "InLoadOrderModuleList" which in the PEB_LDR_DATA structure.
      PLIST_ENTRY pListHead = (PLIST_ENTRY)(ldr + InLoadOrderModuleList_OFFSET);
      PLIST_ENTRY pMod = pListHead->Flink;

      while (pMod != pListHead)
      {
            DbgPrint("Base=%p, Size=%ld, Path=%wZ\n",
                (PVOID)(((PLDR_DATA_TABLE_ENTRY)pMod)->DllBase),
                (ULONG)(((PLDR_DATA_TABLE_ENTRY)pMod)->SizeOfImage),
                &(((PLDR_DATA_TABLE_ENTRY)pMod)->FullDllName));
            pMod = pMod->Flink;
      }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
      DbgPrint("EXCEPTION_EXECUTE_HANDLER is occure...\n");
    }
    KeUnstackDetachProcess(&ks);
}


VOID DriverUnload(IN PDRIVER_OBJECT pDrvObj)
{
    DbgPrint("DriverUnload...\n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDrvObj, IN PUNICODE_STRING RegistryPath)
{
    DbgPrint("DriverEntry...\n");
    pDrvObj->DriverUnload = DriverUnload;

    EnumerateModule((HANDLE)1320);      // explorer.exe's current handle pid.

    return STATUS_SUCCESS;
}

运行结果(只显示一部分结果了哈):


本贴用作学习笔记用于记录遇到的错误和解决方法,便于大家交流讨论和之后的温习。

有关PEB,LDR的只是可以参考一下站里的帖子:
https://www.0xaa55.com/forum.php?mod=viewthread&tid=1385&highlight=LDR
https://www.0xaa55.com/forum.php?mod=viewthread&tid=1363&highlight=PEB

唐凌 发表于 2020-2-5 22:57:48

继续纠结几个细节问题:
1. Lookup的本质是一个引用,你可能忘记了或者不知到要解引用进程对象(ObDereferenceObject)。
2. 获取PEB用硬编码是一种很蛋疼的行为。有个函数叫PsGetProcessPeb,你反汇编这个函数后就可以发现这个偏移量就在函数里。
每个版本的同一体系架构的Windows这个函数都长一个德行。比如x64上它们都长这样:
mov rax,qword ptr
ret
机器码就是48 8B 81 XX XX XX XX C3。那么函数地址+3的4个字节就是偏移量了。
3. PEB和LDR都在用户态内存中,你可以试试写个应用程序。用ZwQueryInformationProcess函数可以拿到PEB,然后再用读进程内存的函数枚举。
4. LDR是一个双向链表,你可以试试摘链隐藏模块,并恢复。如果ARK工具(比如PCHunter)枚举出的模块标红就证明隐藏成功。
5. 你特地声明的三个函数都是已经在MSDN中文档化的函数,它们被包含在了ntifs.h里。可以认为ntifs.h>ntddk.h>wdm.h。
6. 读写进程内存的话使用try-except是给ProbeForRead/Write函数用的,否则try就没什么用,异常也不会进入except里,这是内核try特色。ProbeForRead/Write也是文档化函数,自己查MSDN怎么用吧。

watermelon 发表于 2020-2-6 01:13:44

tangptr@126.com 发表于 2020-2-5 22:57
继续纠结几个细节问题:
1. Lookup的本质是一个引用,你可能忘记了或者不知到要解引用进程对象(ObDerefere ...

PsGetProcessPeb的反汇编的确是那样的,在我的虚拟机上是 mov rax, qword ptr ,正好就是PEB在EPROCESS中的偏移量;
那个这个程序的整体结构是参考Tesla_Angela大佬的那个PDF上的,我认为那个ProbeForRead没有什么实质性的帮助所以当初自己复述程序时候就没有考虑到,看来还是有区别的。
2,3,4的练习我会自己试着写一下!当初写头文件的时候,我在提前include了wdm.h,后来在他下面包含了ntifs.h头文件,结果程序划红线(有错误),我就去掉了ntifs.h头文件,直到包含了ntddk.h头文件,现在才晓得是这样的一个包含顺序!学习了,谢谢tangptr大佬的指导!orz

Ayala 发表于 2020-2-6 04:11:43

本帖最后由 Ayala 于 2020-2-6 13:40 编辑

都Attach了后面直接get就好了
https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-kestackattachprocess
另外硬编码结构偏移不太好吧,汇编现在几乎都不这么写了 自己定义一个简单的结构呢不用的成员忽略了就好了

唐凌 发表于 2020-2-6 13:38:35

watermelon 发表于 2020-2-6 01:13
PsGetProcessPeb的反汇编的确是那样的,在我的虚拟机上是 mov rax, qword ptr ,正好就是PEB ...

再说一点,直接对进程对象搞操作的过程要防止进程突然退出。
进程对象有个锁用来防止进程突然退出,它是Process->RundownProtect。用ExAcquireRundownProtection可以获取锁,用ExReleaseRundownProtection可以释放锁。这些都是导出的函数,不过用这两个操作进程防退出锁需要搞偏移量。
从NT6开始用PsAcquireProcessExitSynchronization/PsReleaseProcessExitSynchronization也可以操作进程防退出锁。参数直接填进程对象地址就行。
如果用ZwXXX函数的话(也就是用句柄操作进程对象)这些操作都被包含了。

watermelon 发表于 2020-2-7 19:07:33

本帖最后由 watermelon 于 2020-2-7 20:18 编辑

tangptr@126.com 发表于 2020-2-5 22:57
继续纠结几个细节问题:
1. Lookup的本质是一个引用,你可能忘记了或者不知到要解引用进程对象(ObDerefere ...

收获超级多!
1.小弟我一开始的确不知道用了PsLookupProcessByProcessId以后要再解引用,然后我又查了一遍这个API的文档,发现了人家说的非常清楚:

2.我在vs2013和wdk8.1上用了ntifs的头文件,但是还是要声明_LDR_DATA_TABLE_ENTRY这个结构体。
3.成功完成了“摘链”的操作,用来移除模块(PChunter64上的确有明确的标记为红色的,并且在最下面):

一开始我摘链的时候还以为只需要将InLoadOrderModuleList的模块的链表结点去除掉就行,但是不成功,PCHunter64上只会将那个链表结点放在最后一个位置,而不是原先的位置;
后来我上网查了一下,发现有一篇帖子是win32上操作的,他将InLoadOrderModuleList、InMemoryOrderModuleList、InInitializationOrderModuleList三个链表全部改变了。
经过几次实验发现,InLoadOrderModuleList、InMemoryOrderModuleList、InInitializationOrderModuleList里面模块的顺序是一样的(多次通过打印_PEB_LDR_DATA中偏移0x010,
0x020,0x030位置处的结构体),所以每次我就按照InLoadOrderModuleList的顺序来找指定的要脱链模块的位置,然后三个链表同时进行改变。
最后是一个摘链以后怎么恢复,我是想的用一个全局变量的结构体来存储当初被摘链的模块在链表上的信息(包括他的地址和他在原先链表上的位置信息),然后进行双向链表的插入操作,
我用windbg调试程序,发现程序每次可以单步运行到末尾,但是程序一运行完毕就会蓝屏,可能是我在当时恢复链表信息的时候姿势不对(我猜是我的恢复时候链表的结点的内容没有写对),
此时需要进一步调试程序。

// Remove and recover module test.
#include <ntddk.h>
#include <windef.h>


// _KAPC_STATE structure is included in "ntifs.h" header file.
typedef struct _KAPC_STATE
{
        LIST_ENTRY ApcListHead;
        PKPROCESS Process;
        UCHAR KernelApcInProgress;
        UCHAR KernelApcPending;
        UCHAR UserApcPending;
} KAPC_STATE, *PKAPC_STATE;

typedef struct _LDR_DATA_TABLE_ENTRY
{
        LIST_ENTRY64        InLoadOrderLinks;
        LIST_ENTRY64        InMemoryOrderLinks;
        LIST_ENTRY64        InInitializationOrderLinks;
        PVOID                        DllBase;
        PVOID                        EntryPoint;
        ULONG                        SizeOfImage;
        UNICODE_STRING        FullDllName;
        UNICODE_STRING         BaseDllName;
        ULONG                        Flags;
        USHORT                        LoadCount;
        USHORT                        TlsIndex;
        PVOID                        SectionPointer;
        ULONG                        CheckSum;
        PVOID                        LoadedImports;
        PVOID                        EntryPointActivationContext;
        PVOID                        PatchInformation;
        LIST_ENTRY64        ForwarderLinks;
        LIST_ENTRY64        ServiceTagLinks;
        LIST_ENTRY64        StaticLinks;
        PVOID                        ContextInformation;
        ULONG64                        OriginalBase;
        LARGE_INTEGER        LoadTime;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

//
// Functions declaration
//
NTKERNELAPI PPEB PsGetProcessPeb(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE Id, PEPROCESS *Process);
NTKERNELAPI VOID NTAPI KeStackAttachProcess(PEPROCESS Process, PKAPC_STATE ApcState);
NTKERNELAPI VOID NTAPI KeUnstackDetachProcess(PKAPC_STATE ApcState);


// Use the pointer of LIST_ENTRY to store the message
// about removed/recovered module.
typedef struct _RECOVER_MODULE
{
        PLIST_ENTRY module;
        INT position;
}RECOVER_MODULE, *PRECOVER_MODULE;


// define a global RECOVER_MODULE structure parameter.
RECOVER_MODULE g_RecoverMod = { 0 };


//
// Get the process's pointer to EPROCESS.
//
PEPROCESS LookupProcess(IN HANDLE hPid)
{
        PEPROCESS eproc = NULL;
        if (NT_SUCCESS(PsLookupProcessByProcessId(hPid, &eproc)))
        {
                return eproc;
        }
        return NULL;
}


//
// Remove the module.
//
VOID RemoveModule(IN HANDLE hPid, IN UNICODE_STRING usModName, IN OUT PRECOVER_MODULE pRecoverMod)
{
        DbgPrint("RemoveModule...\n");
        PEPROCESS eproc = LookupProcess(hPid);
        KAPC_STATE ks;
        INT index = 0;
        if (eproc)
        {
                DbgPrint("Get the valid EPROCESS's address...\n");
        }
        else
        {
                DbgPrint("Can't get the valid EPROCESS's address...\n");
                return;
        }

        // Attach the process.
        KeStackAttachProcess(eproc, &ks);

        // Get the address of PEB.
        ULONG64 peb = (ULONG64)PsGetProcessPeb(eproc);

        // Get the address of LDR.
        ULONG64 ldr = *(PULONG64)((ULONG64)peb + 0x018);

       
        // Get the address of "InLoadOrderModuleList".
        PLIST_ENTRY pLoadListHead = (PLIST_ENTRY)(ldr + 0x010);                // 0x010 is the offset.
        PLIST_ENTRY pLoadMod = pLoadListHead->Flink;

        // Get the address of "InMemoryOrderModuleList".
        PLIST_ENTRY pMemListHead = (PLIST_ENTRY)(ldr + 0x020);                // 0x020 is the offset
        PLIST_ENTRY pMemMod = pMemListHead->Flink;

        // Get the address of "InInitializationOrderModuleList".
        PLIST_ENTRY pInitListHead = (PLIST_ENTRY)(ldr + 0x030);                // 0x030 is the offset
        PLIST_ENTRY pInitMod = pInitListHead->Flink;


        // Try to remove the module.
        while (pLoadMod != pLoadListHead)
        {
                if (RtlEqualUnicodeString(&usModName, &(((PLDR_DATA_TABLE_ENTRY)pLoadMod)->BaseDllName), TRUE))
                {
                        DbgPrint("Find the module:%wZ\n", &usModName);

                        // Remove the module on "InLoadOrderModuleList".
                        pLoadMod->Blink->Flink = pLoadMod->Flink;
                        pLoadMod->Flink->Blink = pLoadMod->Blink;

                        // Remove the module on "InMemoryOrderModuleList".
                        pMemMod->Blink->Flink = pMemMod->Flink;
                        pMemMod->Flink->Blink = pMemMod->Blink;
                       
                        // Remove the module on "InInitializationOrderModuleList".
                        pInitMod->Blink->Flink = pInitMod->Flink;
                        pInitMod->Flink->Blink = pInitMod->Blink;

                        // Store the message of the removed module.
                        pRecoverMod->module = (PLIST_ENTRY)ExAllocatePool(PagedPool, sizeof(LIST_ENTRY));
                        RtlMoveMemory(pRecoverMod->module, pLoadMod, sizeof(LIST_ENTRY));
                        pRecoverMod->position = index;                // Store the position of the removed module.

                        DbgPrint("Modified the module's successfully...\n");
                        break;
                }
               
                pLoadMod = pLoadMod->Flink;
                pMemMod = pMemMod->Flink;
                pInitMod = pInitMod->Flink;
                index++;
        }
       
        // Detach the process.
        KeUnstackDetachProcess(&ks);

        // Release the resource which caused by "PsLookupProcessByProcessId".
        ObDereferenceObject(eproc);
}

//
// Recover the module which has been removed.
// WARNING: this function can be operated successfully, but , maybe some structures
// of LDR that I have made a mistake. So, when use this function, the computer can
// corrupt suddenly.
//
VOID RecoverModule(IN HANDLE hPid, IN RECOVER_MODULE RecoverModule)
{
        PEPROCESS eproc = LookupProcess(hPid);
        KAPC_STATE ks;
        INT index = 0;

        // address of PEB structure.
        ULONG64 peb = (ULONG64)PsGetProcessPeb(eproc);
        // attach the process.
        KeStackAttachProcess(eproc, &ks);

        // address of LDR structure.
        ULONG64 ldr = *(PULONG64)(peb + 0x018);                // 0x018 is the offset.



        // Get the address of "InLoadOrderModuleList".
        PLIST_ENTRY pLoadListHead = (PLIST_ENTRY)(ldr + 0x010);                // 0x010 is the offset.
        PLIST_ENTRY pLoadMod = pLoadListHead->Flink;

        // Get the address of "InMemoryOrderModuleList".
        PLIST_ENTRY pMemListHead = (PLIST_ENTRY)(ldr + 0x020);                // 0x020 is the offset
        PLIST_ENTRY pMemMod = pMemListHead->Flink;

        // Get the address of "InInitializationOrderModuleList".
        PLIST_ENTRY pInitListHead = (PLIST_ENTRY)(ldr + 0x030);                // 0x030 is the offset
        PLIST_ENTRY pInitMod = pInitListHead->Flink;

        while (pLoadMod != pLoadListHead)
        {
                DbgPrint("Inner cycle!!!...\n");
                if (index == RecoverModule.position)
                {
                        PLIST_ENTRY temp;

                        RECOVER_MODULE RecoverModule_1 = { 0 };
                        RECOVER_MODULE RecoverModule_2 = { 0 };

                        RecoverModule_1.module = (PLIST_ENTRY)ExAllocatePool(PagedPool, sizeof(LIST_ENTRY));
                        RecoverModule_2.module = (PLIST_ENTRY)ExAllocatePool(PagedPool, sizeof(LIST_ENTRY));

                        // Make two duplications of "pRecoverModule".
                        RtlMoveMemory(RecoverModule_1.module, RecoverModule.module, sizeof(LIST_ENTRY));
                        RtlMoveMemory(RecoverModule_2.module, RecoverModule.module, sizeof(LIST_ENTRY));


                        // Recover the "InLoadOrderModuleList".
                        temp = pLoadMod->Flink;
                        pLoadMod->Flink = RecoverModule.module;
                        RecoverModule.module->Flink = temp;
                        temp->Blink = RecoverModule.module;
                        RecoverModule.module->Blink = pLoadMod;

                        // Recover the "InMemoryOrderModuleList".
                        temp = pMemMod->Flink;
                        pMemMod->Flink = RecoverModule_1.module;
                        RecoverModule_1.module->Flink = temp;
                        temp->Blink = RecoverModule_1.module;
                        RecoverModule_1.module->Blink = pMemMod;

                        // Recover the "InInitializationOrderModuleList"
                        temp = pInitMod->Flink;
                        pInitMod->Flink = RecoverModule_2.module;
                        RecoverModule_2.module->Flink = temp;
                        temp->Blink = RecoverModule_2.module;
                        RecoverModule_2.module->Blink = pMemMod;

                        break;
                }

                // Because of the structure's content, "pRecoverModule_1" and
                // "pRecoverModule_2" can't be free.
                // ExFreePool(pRecoverModule_1);
                // ExFreePool(pRecoverModule_2);

                pLoadMod = pLoadMod->Flink;
                pMemMod = pMemMod->Flink;
                pInitMod = pInitMod->Flink;
                index++;
        }
        ObDereferenceObject(eproc);
}


VOID DriverUnload(IN PDRIVER_OBJECT pDrvObj)
{
        DbgPrint("DriverUnload...\n");
        // Release the allocated buffer of "g_RecoverMod.module".
        ExFreePool(g_RecoverMod.module);
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDrvObj, IN PUNICODE_STRING RegistryPath)
{
        DbgPrint("DriverEntry...\n");
        pDrvObj->DriverUnload = DriverUnload;

        // The name of module which you want to remove.
        UNICODE_STRING usModName;

        // Try to remove the kernel32.dll of the "explorer.exe".
        RtlInitUnicodeString(&usModName, L"kernel32.dll");

        RemoveModule((HANDLE)1520, usModName, &g_RecoverMod);

        //RecoverModule((HANDLE)1396, g_RecoverMod);
       
        return STATUS_SUCCESS;

}


接下来需要接着调试程序和完成Ring3上面ZwQueryInformationProcess的PEB操作。
Or2

watermelon 发表于 2020-2-7 20:17:09

Ayala 发表于 2020-2-6 04:11
都Attach了后面直接get就好了
https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-n ...

哦,就是用自己声明一下的结构体,然后用->操作来写是嘛,那样也是一个非常不错的办法!orz

Golden Blonde 发表于 2020-2-9 01:42:53

楼主试试把PEB32也给枚举出来,提示:Wow64Process是PEB32的指针,用PsGetProcessWow64Process获取。

watermelon 发表于 2020-3-13 17:09:03

美俪女神 发表于 2020-2-9 01:42
楼主试试把PEB32也给枚举出来,提示:Wow64Process是PEB32的指针,用PsGetProcessWow64Process获取。 ...

哦哦好的!!超感谢“美丽女神“管理员的指导!
小弟我试试。
页: [1]
查看完整版本: 【C语言】利用PEB和LDR遍历指定进程的模块