A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about kernel-mode development.
 #18195  by myid
 Wed Feb 13, 2013 6:28 pm
Hello, everyone, I meet a very strange problem, maybe some one will not believe my word:
If I delete a DbgPrint in code below, the function will not work.
"Not work" means this function cannot enumerate process modules, without any exceptions or BSOD. Why?
Code: Select all
typedef enum _MEMORY_INFORMATION_CLASS
{
    MemoryBasicInformation,
    MemoryWorkingSetList,
    MemorySectionName,
    MemoryBasicVlmInformation
} MEMORY_INFORMATION_CLASS;

typedef struct _MEMORY_BASIC_INFORMATION {
  PVOID  BaseAddress;
  PVOID  AllocationBase;
  DWORD  AllocationProtect;
  SIZE_T RegionSize;
  DWORD  State;
  DWORD  Protect;
  DWORD  Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

typedef struct _MEMORY_SECTION_NAME   // Information Class 2
{
    UNICODE_STRING SectionFileName;
} MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;

NTKERNELAPI NTSTATUS __fastcall ZwQueryVirtualMemory
(
    IN HANDLE ProcessHandle,
    IN PVOID BaseAddress,
    IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
    OUT PVOID MemoryInformation,
    IN ULONG MemoryInformationLength,
    OUT PULONG ReturnLength OPTIONAL
);

VOID EnumModuleByQueryVM(PEPROCESS Process)
{
    KAPC_STATE ks;
    NTSTATUS Status=0;
    HANDLE hProcess=(HANDLE)(-1);
    PVOID MemBase=NULL;
    SIZE_T MemSize=0;
    MEMORY_BASIC_INFORMATION MemBasicInfo={0};
    PMEMORY_SECTION_NAME MemSectionName=NULL;
    ULONG MemSectionNameLength=0;
    ULONG ReturnLength=0,i=0,PmCnt=0;
    //Init data array
    PmCnt=0;
    memset(ModuleInfo2,0,512 * sizeof(MODULE_INFO));
    //Enum modules
    KeStackAttachProcess(Process, &ks);
    __try
    {
        while (MemBase<(PVOID)0x7FFFFFFFFFFF)
        {
            //Query Basic Information
            Status=ZwQueryVirtualMemory(hProcess,
                                        MemBase,
                                        MemoryBasicInformation,
                                        &MemBasicInfo,
                                        sizeof(MemBasicInfo),
                                        &ReturnLength);
            if (Status==0)
            {
                MemSize=MemBasicInfo.RegionSize;
                //
                //IF DELETE THIS DbgPrint, THIS FUNCTION CANNOT ENUMERATE PROCESS MODULES!!! WHY???
                //
                DbgPrint("BaseAddress:0x%p\tAllocationBase:0x%p\tAllocationProtect:0x%x\tRegionSize:0x%x\tState:0x%x\tProtect:0x%x\tType:0x%x\n",
                            MemBasicInfo.BaseAddress,
                            MemBasicInfo.AllocationBase,
                            MemBasicInfo.AllocationProtect,
                            MemBasicInfo.RegionSize,
                            MemBasicInfo.State,
                            MemBasicInfo.Protect,
                            MemBasicInfo.Type);
                //Query Image Name
                Status=ZwQueryVirtualMemory(hProcess,
                                            MemBase,
                                            MemorySectionName,
                                            NULL,
                                            0,
                                            &ReturnLength);
                MemSectionNameLength=ReturnLength;
                MemSectionName=(PMEMORY_SECTION_NAME)kmalloc(MemSectionNameLength);    //LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,MemSectionNameLength);
                memset(MemSectionName,0,MemSectionNameLength);
                MemSectionName->SectionFileName.Buffer=(PWCHAR)((PCHAR)MemSectionName + sizeof (MEMORY_SECTION_NAME));
                MemSectionName->SectionFileName.MaximumLength=(USHORT)(MemSectionNameLength -sizeof sizeof (MEMORY_SECTION_NAME));
                //
                //IF DELETE DbgPrint ABOVE, THIS ZwQueryVirtualMemory WILL NEVER RETURN STATUS_SUCCESS
                //
                Status=ZwQueryVirtualMemory(hProcess,
                                            MemBase,
                                            MemorySectionName,
                                            MemSectionName,
                                            MemSectionNameLength,
                                            &ReturnLength);
                DbgPrint("ZwQueryVirtualMemory~MemorySectionName status: %x\n",Status);
                if (NT_SUCCESS(Status))
                {
                    PmCnt++;
                    DbgPrint("[QVM_EM]%p\t%wZ",MemBasicInfo.AllocationBase,&(MemSectionName->SectionFileName));
                }
                kfree(MemSectionName);
                MemBase=(PCHAR)MemBase+MemSize; 
            }
            else
            {
                DbgPrint("Enum memory finished: %x\tCount: %ld\n",Status,PmCnt);
                break;
            }
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        DbgPrint("[QVM_EM]EXCEPTION_EXECUTE_HANDLER\n");
    }
    KeUnstackDetachProcess(&ks);
}
 #18203  by EP_X0FF
 Thu Feb 14, 2013 6:50 am
Hello,

what are the benefits of this kernel mode brute-force method? VirtualQueryEx from x64 user mode application will give you same results but without this bugfest. If you aware of protected processes or processes that can't be opened, supply full access handle from driver down to your user mode application. However this is dangerous in meaning of security.

As for your code, comment everything except failing ZwQueryVirtualMemory calls and show every NTSTATUS result. Nobody will not compile yours code to find this yourself.
 #18205  by myid
 Thu Feb 14, 2013 9:34 am
EP_X0FF wrote:Hello,

what are the benefits of this kernel mode brute-force method? VirtualQueryEx from x64 user mode application will give you same results but without this bugfest. If you aware of protected processes or processes that can't be opened, supply full access handle from driver down to your user mode application. However this is dangerous in meaning of security.

As for your code, comment everything except failing ZwQueryVirtualMemory calls and show every NTSTATUS result. Nobody will not compile yours code to find this yourself.
Hello, EP_X0FF, I just want to know what lead to this strange result. And I have fun on programming.
If the DbgPrint not exist, the third ZwQueryVirtualMemory always return 0xC000000D(STATUS_INVALID_PARAMETER).
If the DbgPrint exist, the third ZwQueryVirtualMemory sometimes return 0x0, that means I can get the image name of that memory region.
 #18207  by EP_X0FF
 Thu Feb 14, 2013 10:06 am
You have corrupted stack. Remove "fastcall" from function declaration and fix MEMORY_BASIC_INFORMATION as it declared in MSDN, see Remarks, meaning that all instances of it must start on a 16 boundary. ZwQueryVirtualMemory is undocumented and should not be used.
 #18208  by myid
 Thu Feb 14, 2013 12:12 pm
EP_X0FF wrote:You have corrupted stack. Remove "fastcall" from function declaration and fix MEMORY_BASIC_INFORMATION as it declared in MSDN, see Remarks, meaning that all instances of it must start on a 16 boundary. ZwQueryVirtualMemory is undocumented and should not be used.
My program is running under Windows X64, so I think use __fastcall is not wrong.
And I fix the MEMORY_BASIC_INFORMATION struct as this definition:
typedef struct DECLSPEC_ALIGN(16) _MEMORY_BASIC_INFORMATION64 {
ULONGLONG BaseAddress;
ULONGLONG AllocationBase;
DWORD AllocationProtect;
DWORD __alignment1;
ULONGLONG RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
DWORD __alignment2;
} MEMORY_BASIC_INFORMATION64, *PMEMORY_BASIC_INFORMATION64;
But the problem is still existing.
 #18209  by Vrtule
 Thu Feb 14, 2013 1:12 pm
[query]
My program is running under Windows X64, so I think use __fastcall is not wrong.
And I fix the MEMORY_BASIC_INFORMATION struct as this definition:
[/query]
Use NTAPI instead of fastcall. It is a portable way how to declare a calling convention for functions exported by the kernel. Anyway, I am not sure whether the kernel uses fastcall or cdecl.
 #18210  by EP_X0FF
 Thu Feb 14, 2013 1:46 pm
Vrtule wrote:Use NTAPI instead of fastcall. It is a portable way how to declare a calling convention for functions exported by the kernel. Anyway, I am not sure whether the kernel uses fastcall or cdecl.
The only one calling conversion for x64 is fastcall. Anything else is ignored and assumed as fastcall. Techinically myid is correct, but no one not declare calling conversion clearly as "fastcall" because it is confusing and will be a problem with cross-platform compilation.

http://msdn.microsoft.com/en-us/library/9b372w95.aspx
http://msdn.microsoft.com/en-us/magazine/cc300794.aspx
myid wrote:But the problem is still existing.
Ok, turn off everything except first two ZwQueryVirtualMemory calls and watch what they return, something like this (i have roughly modified your code).
Code: Select all
VOID EnumModuleByQueryVM(PEPROCESS Process)
{
    KAPC_STATE ks;
    NTSTATUS Status=0;
    HANDLE hProcess=(HANDLE)(-1);
    PVOID MemBase=NULL;
    SIZE_T MemSize=0;
    MEMORY_BASIC_INFORMATION MemBasicInfo={0};
    PMEMORY_SECTION_NAME MemSectionName=NULL;
    ULONG MemSectionNameLength=0;
    ULONG ReturnLength=0,i=0,PmCnt=0;
    //Init data array
    PmCnt=0;
    //Enum modules
    KeStackAttachProcess(Process, &ks);
    __try
    {
        while (MemBase<(PVOID)0x7FFFFFFFFFFF)
        {
            //Query Basic Information
            Status=ZwQueryVirtualMemory(hProcess,
                                        MemBase,
                                        MemoryBasicInformation,
                                        &MemBasicInfo,
                                        sizeof(MemBasicInfo),
                                        &ReturnLength);
            if (NT_SUCCESS(Status))
            {
                MemSize = MemBasicInfo.RegionSize;
                
		//Query Image Name
                Status=ZwQueryVirtualMemory(hProcess,
                                            MemBase,
                                            MemorySectionName,
                                            NULL,
                                            0,
                                            &ReturnLength);

		DbgPrint("%lx, %lu", Status, ReturnLength);
                MemBase=(PCHAR)MemBase+MemSize; 
            }
            else
            {
                DbgPrint("Enum memory finished: %x\tCount: %ld\n",Status,PmCnt);
                break;
            }
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        DbgPrint("[QVM_EM]EXCEPTION_EXECUTE_HANDLER\n");
    }
    KeUnstackDetachProcess(&ks);
}
 #18211  by myid
 Thu Feb 14, 2013 3:43 pm
EP_X0FF wrote:
Vrtule wrote:Use NTAPI instead of fastcall. It is a portable way how to declare a calling convention for functions exported by the kernel. Anyway, I am not sure whether the kernel uses fastcall or cdecl.
The only one calling conversion for x64 is fastcall. Anything else is ignored and assumed as fastcall. Techinically myid is correct, but no one not declare calling conversion clearly as "fastcall" because it is confusing and will be a problem with cross-platform compilation.

http://msdn.microsoft.com/en-us/library/9b372w95.aspx
http://msdn.microsoft.com/en-us/magazine/cc300794.aspx
myid wrote:But the problem is still existing.
Ok, turn off everything except first two ZwQueryVirtualMemory calls and watch what they return, something like this (i have roughly modified your code).
Code: Select all
VOID EnumModuleByQueryVM(PEPROCESS Process)
{
    KAPC_STATE ks;
    NTSTATUS Status=0;
    HANDLE hProcess=(HANDLE)(-1);
    PVOID MemBase=NULL;
    SIZE_T MemSize=0;
    MEMORY_BASIC_INFORMATION MemBasicInfo={0};
    PMEMORY_SECTION_NAME MemSectionName=NULL;
    ULONG MemSectionNameLength=0;
    ULONG ReturnLength=0,i=0,PmCnt=0;
    //Init data array
    PmCnt=0;
    //Enum modules
    KeStackAttachProcess(Process, &ks);
    __try
    {
        while (MemBase<(PVOID)0x7FFFFFFFFFFF)
        {
            //Query Basic Information
            Status=ZwQueryVirtualMemory(hProcess,
                                        MemBase,
                                        MemoryBasicInformation,
                                        &MemBasicInfo,
                                        sizeof(MemBasicInfo),
                                        &ReturnLength);
            if (NT_SUCCESS(Status))
            {
                MemSize = MemBasicInfo.RegionSize;
                
		//Query Image Name
                Status=ZwQueryVirtualMemory(hProcess,
                                            MemBase,
                                            MemorySectionName,
                                            NULL,
                                            0,
                                            &ReturnLength);

		DbgPrint("%lx, %lu", Status, ReturnLength);
                MemBase=(PCHAR)MemBase+MemSize; 
            }
            else
            {
                DbgPrint("Enum memory finished: %x\tCount: %ld\n",Status,PmCnt);
                break;
            }
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        DbgPrint("[QVM_EM]EXCEPTION_EXECUTE_HANDLER\n");
    }
    KeUnstackDetachProcess(&ks);
}
OK, I will try to do as you said. Thank you very much.