Я пишу драйвер для дампов процесса, и в ходе написания появился один вопросик. Я юзаю такую структурку: typedef struct { // Input IN ULONG uProcessID; IN ULONG uVirtualAddress; IN ULONG uSize; // Output OUT ULONG uStatus; OUT LPVOID lpAllocatedMemory; } PT_DUMP_PROCESS_MEMORY; (lpAllocatedMemory - выделяю в приложении. Размер=uSize) Через DeviceIoControl отсылаю её драйверу. Но он почему-то ничего не возвращает. Но если lpAllocatedMemory сделать статическим (lpAllocatedMemory[255]), то всё Ок. 255 байт пересылаются. Не подскажете где ошибка? Мне нужно именно динамически менять размер lpAllocatedMemory.
Подробнее. Какой метод в/в ты используешь: METHOD_BUFFERED, METHOD_NEITHER...? Драйвер твой? Если ты меняешь размер буфера, как драйвер об этом узнаёт?
Драйвер мой. Метод такой: #define IOCTL_GET_PROCESS_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS); Размер буффера драйвер узнаёт из uSize. Вот кусок кода, только тут теперь драйвер сам пишет память на диск (функция SaveBuffer). Ошибок нет. Код (Text): switch(pStack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_GET_PROCESS_MEMORY: { // Install SEH __try { // Get Process ID uPID = ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uProcessID; // Get Virtual Address uVirtualAddress = ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uVirtualAddress; // Get size uSize = ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uSize; DbgPrint("DeviceIoControl: uPID=0x%0.8X uVirtualAddress=0x%0.8X uSize=0x%0.8X OutSize=%d\n", uPID, uVirtualAddress, uSize, out_size); ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uStatus = 0; if(NT_SUCCESS(PsLookupProcessByProcessId((DWORD)uPID, &eprocess))) { KeAttachProcess(eprocess); DbgPrint("PEToolsDrvDevice: RtlCopyMemory\n"); LPVOID lpBuffer = ExAllocatePool(NonPagedPool, uSize); RtlCopyMemory(lpBuffer, (const void *)((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uVirtualAddress, uSize); SaveBuffer("\\??\\C:\\dump.bin", lpBuffer, uSize); Irp->IoStatus.Information = sizeof(PT_DUMP_PROCESS_MEMORY); ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uStatus = 1; } KeDetachProcess(); ObDereferenceObject((void *)eprocess); } __except(EXCEPTION_EXECUTE_HANDLER) { KeDetachProcess(); ObDereferenceObject((void *)eprocess); DbgPrint("DeviceIoControl: Access violation\n"); ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uStatus = 0; } break; А до этого, я просто копировал память при помощи RtlCopyMemory: RtlCopyMemory((const void *)((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->lpAllocatedMemory, (const void *)((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uVirtualAddress, uSize); Но так ничего не копировалось был Access Violation.
Понятно. ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->lpAllocatedMemory - это указатель на буфер в юзерном адресном пространстве твоего! процесса. Когда ты делаешь KeAttachProcess, ты отключаешься от своего юзерного адресного пространства и пытаешься копировать в несуществующий буфер в приаттаченом процессе. Тут двы выхода: лочить/отображать свой буфер в ядро, для того чтобы он был доступен в любом контексте или выделять временный буфер в ядре, аттачится, копировать во временный буфер, детачится и копировать из временного буфера к себе в юзерный буфер. И в вышеприведенном коде у тя как минимум четыре явных бага и несколько потенциальных и NonPagedPool - это лишнее. Я бы переписАл его так (дебужные мессаги убрал чтоб не мешали и сам мог где-нить ошибиться, ибо некогда): Код (Text): case IOCTL_GET_PROCESS_MEMORY: BOOLEAN fOK = FALSE; uPID = ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uProcessID; uVirtualAddress = ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uVirtualAddress; uSize = ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uSize; ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uStatus = 0; if ( NT_SUCCESS(PsLookupProcessByProcessId((DWORD)uPID, &eprocess)) ) { LPVOID lpBuffer = ExAllocatePool( PagedPool, uSize ); if ( NULL != lpBuffer ) { KeAttachProcess( eprocess ); __try { ProbeForRead( uVirtualAddress, uSize, sizeof(UCHAR) ); RtlCopyMemory( lpBuffer, uVirtualAddress, uSize ); fOK = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } KeDetachProcess(); if ( fOK ) { __try { ProbeForWrite( ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->lpAllocatedMemory, uSize, sizeof(UCHAR) ); RtlCopyMemory( ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->lpAllocatedMemory, lpBuffer, uSize ); Irp->IoStatus.Information = sizeof(PT_DUMP_PROCESS_MEMORY); ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->uStatus = 1; } __except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } } ExFreePool( lpBuffer ); } ObDereferenceObject( (void *) eprocess ); } break; Или, как я говорил, отобрази свой буфер в ядро. Так он буде доступен в любом контексте. Код (Text): PMDL mdl = NULL; mdl = IoAllocateMdl( ((PT_DUMP_PROCESS_MEMORY *)pWorkBuffer)->lpAllocatedMemory, uSize, FALSE, TRUE, NULL ); MmProbeAndLockPages( mdl, UserMode, IoWriteAccess ); lpBuffer = MmGetSystemAddressForMdlSafe( mdl, NormalPagePriority );
Проверил, работает так как я и хотел. Но появился ещё один вопросик. Как можно сдампить "NO ACCESS/NONE" регионы? Догадываюсь, что это делается как в usermode (OpenPorcess/VirtualProctectEx потом дамп). Но для драйвера я нашёл только аналог OpenProcess Или как-то по другому?
Вот с этим делом в ядре большие проблемы, в том смысле, что ни хрена не экспортируется. Единственная экспортируемая функция - это MmProtectMdlSystemAddress, да и то только для ядерных адресов и для ХР+. Можешь попробовать связку ObOpenObjectByPointer/ZwProtectVirtualMemory, но придется найти точку входа ZwProtectVirtualMemory. Либо попытаться делать то, что делает эта функция. А сделать надо до смешного мало: Код (Text): MiProtectVirtualMemory( Process, &Base, &Size, NewProtect, &OldProtect ); MiProtectVirtualMemory, естественно, тоже не экспортируется.