Намедни решил попробовать написать драйвер под Windows. Варианты с "Hello world" показались унылыми, поэтому в качестве тренировки поставил перед собой следющую цель: написать драйвер, который будет контролировать целостность кода процесса по запросу. В общем, драйвер будет считывать данные о загруженных в память секциях, проверять атрибуты и считать простенькую контрольную сумму, а при повторном обращении - сверять её. Совсем детально описывать процесс я не буду, так как в интернете есть куча мануалов по самым основам, да и желающие могут просто посмотреть примеры из WDK, которые достаточно хорошо документированы.
Код, описанный ниже, предполагается вызывать в обработчике IOCTL-запросов. Начнем с реализации основной функции, которая будет осуществлять проверку, и нескольких вспомогательных.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <Ntifs.h> #include <Ntddk.h> #include <ntstrsafe.h> #if 1 #define CKSMLOG DbgPrint #else #define CKSMLOG(...) #endif /* Переменная, которая будет хранить перечень модулей процесса, */ /* их контрольные суммы и некоторые другие данные. */ /* Данные хранятся в двусвязном списке. */ static LIST_ENTRY ChecksumList; /* Функция инициализации вышеописанной переменной */ VOID ChecksumInit() { InitList(&ChecksumList); } /* Очистка её же */ VOID ChecksumFinalize() { CleanList(&ChecksumList); } /* Функция-прослойка, возвращающая результат проверки целостности вызывающего процесса */ BOOLEAN CheckUserProcess() { return ProcessModules(&ChecksumList, (UINT32) PsGetCurrentProcessId()); } |
Функции работы с двусвязным списком я подробно разбирать не буду, так как логика работы довольно тривиальная и подробно описана в MSDN. Опишу лишь формат, в котором я храню данные о секциях модулей.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
typedef struct { /* Идентификатор процесса */ UINT32 Pid; /* Адрес, по которому загружен модуль */ PVOID BaseAddr; /* Относительный виртуальный адрес секции */ UINT32 RVA; /* Имя секции */ UINT8 Name[8]; /* CRC секции */ UINT32 CRC32; /* Указатель на следующий элемент списка */ LIST_ENTRY Next; /* Вспомогательная переменная для очистки списка от выгруженных модулей */ UINT32 IsPresent; } SectionList, * pSectionList; |
Теперь рассмотрим основные функции, реализующие заявленный контроль целостности:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
BOOLEAN ProcessModules(PLIST_ENTRY Sections, UINT32 Pid) { PPEB Peb; PEPROCESS Pep; PLDR_DATA_TABLE_ENTRY LdrEntry; PLIST_ENTRY Entry; BOOLEAN Status = FALSE, Error; /* Получаем указатель на структуру EPROCESS для заданного PID */ Pep = GetProcessPep(Pid); if(Pep == NULL) return Status; /* Из EPROCESS получаем PEB */ /* (http://en.wikipedia.org/wiki/Process_Environment_Block) */ Peb = GetProcessPeb(Pep); if(Peb == NULL) return Status; CKSMLOG("(%d) PEB: %p", __LINE__, Peb); /* Проходим по списку загруженных в память модулей */ for(Entry = Peb->Ldr->InMemoryOrderModuleList.Flink; &Peb->Ldr->InMemoryOrderModuleList != Entry; Entry = Entry->Flink) { LdrEntry = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); /* Обрабатываем секции для каждого именованного модуля */ if(LdrEntry->FullDllName.Buffer != NULL) { CKSMLOG("(%d) %p - %u - %wZ", __LINE__, LdrEntry->DllBase, LdrEntry->SizeOfImage, &LdrEntry->FullDllName); ProcessSections(Sections, Pep, LdrEntry->DllBase, LdrEntry->SizeOfImage, Pid, &Error); if(Error) Status = TRUE; } } /* Удалим из списка все секции, связанные с текущим Pid, которые не были обработаны в цикле выше */ DeleteUnusedEntries(Sections, Pid); /* Сбросим метки присутствия для секций */ ZeroIsPresent(Sections, Pid); return Status; } PEPROCESS GetProcessPep(UINT32 Pid) { PEPROCESS Pep = NULL; NTSTATUS Status = PsLookupProcessByProcessId((HANDLE) Pid, &Pep); if(!NT_SUCCESS(Status)) CKSMLOG("(%d) PsLookupProcessByProcessId (PID=%u; Status=%08X)", __LINE__, Pid, Status); return Pep; } PPEB GetProcessPeb(PEPROCESS Pep) { /* Недокументированная (в MSDN) функция, которая просто возвращает элемент из структуры PEPROCESS */ PPEB Peb = PsGetProcessPeb(Pep); /* MSDN не рекомендует использование этой функции, но пусть будет */ if(!MmIsAddressValid(Peb)) { CKSMLOG("(%d) MmIsAddressValid (Addr=%p)", __LINE__, Peb); return NULL; } return Peb; } |
И, наконец, здоровенная функция, которая обрабатывает секции модулей:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
VOID ProcessSections(PLIST_ENTRY Sections, PEPROCESS Pep, PVOID BaseAddr, ULONG ImageSize, UINT32 Pid, PBOOLEAN Error) { /* Указатель на память процесса, которая будет спроецированна в ядро */ PVOID mPtr; UINT32 i, FirstSection, IsMonitored, CRC32; /* http://msdn.microsoft.com/en-us/library/windows/hardware/ff565421%28v=vs.85%29.aspx */ PMDL pMdl = NULL; BOOLEAN IsX64 = FALSE; /* Структуры для разбора PE-файлов, взятые из библиотеки dx'a, но слегка адаптированные под ядро */ /* http://code.google.com/p/portable-executable-library/ */ pimage_dos_header DosHeader; pimage_nt_headers32 NtHeader32; pimage_nt_headers64 NtHeader64; pimage_file_header FileHeader; pimage_section_header SectionHeader; pSectionList SectList; *Error = FALSE; /* Проецируем память процесса в ядро */ mPtr = AllocVaPtr(Pep, BaseAddr, ImageSize, &pMdl); if(mPtr == NULL) { CKSMLOG("(%d) Can't map VA", __LINE__); return; } /* Получаем указатель на DOS-заголовок модуля и проверяем разрядность процесса */ DosHeader = (pimage_dos_header)mPtr; IsX64 = IsX64Process( (pimage_nt_headers32) ((UINT8 *)DosHeader + DosHeader->e_lfanew) ); /* Получаем указатель на FileHeader в зависимости от разрядности */ if(IsX64) { NtHeader64 = (pimage_nt_headers64) ((UINT8 *)DosHeader + DosHeader->e_lfanew); FileHeader = (pimage_file_header)&NtHeader64->FileHeader; } else { NtHeader32 = (pimage_nt_headers32) ((UINT8 *)DosHeader + DosHeader->e_lfanew); FileHeader = (pimage_file_header)&NtHeader32->FileHeader; } /* Получаем указатель на первую секцию */ FirstSection = DosHeader->e_lfanew + FileHeader->SizeOfOptionalHeader + sizeof(image_file_header) + sizeof(UINT32); /* Проходим по всем загруженным секциям модуля */ for(i = 0; i < FileHeader->NumberOfSections; i++) { SectionHeader = (pimage_section_header)((ULONG_PTR)mPtr + FirstSection + (i * sizeof(image_section_header))); /* Нас интересуют только секции с флагами Read, Execute, Contains code, Not Discardable */ IsMonitored = (SectionHeader->Characteristics & image_scn_mem_execute) && (SectionHeader->Characteristics & image_scn_mem_read) && (SectionHeader->Characteristics & image_scn_cnt_code) && !(SectionHeader->Characteristics & image_scn_mem_discardable) ; if(IsMonitored) { /* Вычисляем контрольную сумму секции и проверяем, нет ли записи соответствующей нашей секции в двусвязаном списке */ CRC32 = Crc32Buf((UINT8 *)((ULONG_PTR)mPtr + SectionHeader->VirtualAddress), SectionHeader->Misc.VirtualSize); SectList = FindEntry(Sections, Pid, BaseAddr, SectionHeader->VirtualAddress, SectionHeader->Name); /* Если такой записи нет, то добавим секцию в список */ if(SectList == NULL) { CKSMLOG("(%d) adding entry (PID=%u; BaseAddr=%08X; RVA=%08X; Name=%.8s; CRC32=%08X)", __LINE__, Pid, BaseAddr, SectionHeader->VirtualAddress, SectionHeader->Name, CRC32); AddEntry(sections, Pid, BaseAddr, SectionHeader->VirtualAddress, SectionHeader->Name, CRC32); } /* Если запись есть, то сверим CRC и пометим секцию, как присутствующую в памяти */ else if(SectList->CRC32 == CRC32) { CKSMLOG("(%d) checksum validated (PID=%u; BaseAddr=%08X; RVA=%08X; Name=%.8s; CRC32=%08X)", __LINE__, SectList->Pid, SectList->BaseAddr, SectList->RVA, SectList->Name, SectList->CRC32); SectList->IsPresent = 1; } /* В противном случае пометим секцию, но сообщим об ошибке проверки целостности */ else { CKSMLOG("(%d) erroneous checksum (PID=%u; BaseAddr=%08X; RVA=%08X; Name=%.8s; CRC32=%08X~%08X)", __LINE__, SectList->Pid, SectList->BaseAddr, SectList->RVA, SectList->Name, SectList->CRC32, CRC32); SectList->IsPresent = 1; *Error = TRUE; } } } /* Освободим указатель на user-mode память */ FreeVaPtr(pMdl, BaseAddr); } BOOLEAN IsX64Process(pimage_nt_headers32 NtHeader) { return NtHeader->OptionalHeader.Magic == image_nt_optional_hdr64_magic ? TRUE : FALSE; } /* Проецируем юзермодный виртуальный адрес в ядро */ PVOID AllocVaPtr(PEPROCESS Pep, PVOID VA, ULONG VaSize, PMDL * OutMdl) { KAPC_STATE kAPC; PMDL pMdl; PVOID Ptr = NULL; CKSMLOG("(%d) AllocVaPtr", __LINE__); /* "Подключаем" текущий поток к адресному пространству пользовательского процесса */ KeStackAttachProcess(Pep, &kAPC); /* Получаем указатель на список физических страниц для заданного диапазона виртуальных адресов */ pMdl = IoAllocateMdl(VA, VaSize, FALSE, FALSE, NULL); if(pMdl == NULL) { CKSMLOG("(%d) IoAllocateMdl", __LINE__); KeUnstackDetachProcess(&kAPC); return NULL; } /* Закрепляем страницы в памяти, а то вдруг их кто-нибудь, например, в своп скинуть попытается */ __try { MmProbeAndLockPages(pMdl, UserMode, IoReadAccess); } __except(EXCEPTION_EXECUTE_HANDLER) { CKSMLOG("(%d) MmProbeAndLockPages", __LINE__); IoFreeMdl(pMdl); KeUnstackDetachProcess(&kAPC); return NULL; } /* Проецируем физические страницы в память ядра по произвольному виртуальному адресу */ __try { Ptr = MmMapLockedPagesSpecifyCache(pMdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority); } __except(EXCEPTION_EXECUTE_HANDLER) { CKSMLOG("(%d) MmMapLockedPagesSpecifyCache", __LINE__); MmUnlockPages(pMdl); IoFreeMdl(pMdl); KeUnstackDetachProcess(&kAPC); return NULL; } if(Ptr == NULL) { CKSMLOG("(%d) MmMapLockedPagesSpecifyCache (NULL)", __LINE__); MmUnlockPages(pMdl); IoFreeMdl(pMdl); KeUnstackDetachProcess(&kAPC); return NULL; } /* Отсоединяем текущий поток от пользовательского процесса */ KeUnstackDetachProcess(&kAPC); *OutMdl = pMdl; return Ptr; } /* Освобождаем спроецированную память и дескриптор физических страниц */ VOID FreeVaPtr(PMDL pMdl, PVOID VA) { CKSMLOG("(%d) FreeVaPtr", __LINE__); MmUnmapLockedPages(VA, pMdl); MmUnlockPages(pMdl); IoFreeMdl(pMdl); } |
Вот мы и рассмотрели основной код, позволяющий осуществить простой контроль целостности секций юзермодного процесса из ядра. Дополнительные процедуры и заголовоные файлы: для работы с двусвязными списками, структуры, описывающие заголовки PE-файла и простой табличный метод расчета CRC приведены в архиве ниже.
Теперь возьмем этот код и добавим к какому-нибудь базовому прототипу драйвера. Результат будет выглядеть, например, следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
#include "..\ChecksumLib\ChecksumFunc.h" typedef struct _SampleDeviceExt { PDEVICE_OBJECT Fdo; UNICODE_STRING Symlink; KMUTEX Mutex; } SAMPLE_DEVICE_EXT, *PSAMPLE_DEVICE_EXT; #define DEV_NAME L"SampleDriver" #define SYMLINK_NAME L"\\??\\" DEV_NAME #define IOCTL_SAMPLE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) #pragma code_seg("INIT") VOID DrvUnload(PDRIVER_OBJECT pDriverObject); NTSTATUS DispatchRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp); NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { NTSTATUS Status = STATUS_SUCCESS; PDEVICE_OBJECT Fdo; UNICODE_STRING DevName; PSAMPLE_DEVICE_EXT Pdx; UNICODE_STRING Symlink; UINT16 i = 0; DbgPrint("(%d) DriverEntry", __LINE__); for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) DriverObject->MajorFunction[i] = DispatchRoutine; DriverObject->MajorFunction[i] = NULL; DriverObject->DriverUnload = DrvUnload; RtlInitUnicodeString(&DevName, L"\\Device\\" DEV_NAME); Status = IoCreateDevice ( DriverObject, sizeof(SAMPLE_DEVICE_EXT), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &Fdo ); if(!NT_SUCCESS(Status)) return Status; Pdx = (PSAMPLE_DEVICE_EXT) Fdo->DeviceExtension; Pdx->Fdo = Fdo; DbgPrint("(%d) FDO=%08X, DevExt=%X", __LINE__, Fdo, Pdx); RtlInitUnicodeString(&Symlink, SYMLINK_NAME); Pdx->Symlink = Symlink; Status = IoCreateSymbolicLink(&Symlink, &DevName); if(!NT_SUCCESS(Status)) { IoDeleteDevice(Fdo); return Status; } KeInitializeMutex(&Pdx->Mutex, 0); ChecksumInit(); DbgPrint("(%d) Driver loaded", __LINE__); return Status; } #pragma code_seg() NTSTATUS DispatchRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp) { PSAMPLE_DEVICE_EXT Pdx; PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; Pdx = (PSAMPLE_DEVICE_EXT) DeviceObject->DeviceExtension; KeWaitForMutexObject(&Pdx->Mutex, UserRequest, KernelMode, FALSE, NULL); switch (pIrpStack->MajorFunction) { case IRP_MJ_CREATE: DbgPrint("(%d) IRP_MJ_CREATE", __LINE__); break; case IRP_MJ_CLOSE: DbgPrint("(%d) IRP_MJ_CLOSE", __LINE__); break; case IRP_MJ_DEVICE_CONTROL: DbgPrint("(%d) IRP_MJ_DEVICE_CONTROL", __LINE__); switch (pIrpStack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_SAMPLE: { BOOLEAN Status; UINT32 OutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; if(OutSize != sizeof(BOOLEAN)) { Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; break; } Status = CheckUserProcess(); RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &Status, sizeof(BOOLEAN)); Irp->IoStatus.Information = sizeof(BOOLEAN); } break; } break; default: DbgPrint("(%d) Not implemented major function call", __LINE__); Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; break; } KeReleaseMutex(&Pdx->Mutex, FALSE); IoCompleteRequest(Irp, IO_NO_INCREMENT); return Irp->IoStatus.Status; } VOID DrvUnload(PDRIVER_OBJECT pDriverObject) { PDEVICE_OBJECT NextDevObj; int i; DbgPrint("(%d) DrvUnload", __LINE__); NextDevObj = pDriverObject->DeviceObject; for(i = 0; NextDevObj != NULL; i++) { PSAMPLE_DEVICE_EXT Dx = (PSAMPLE_DEVICE_EXT) NextDevObj->DeviceExtension; UNICODE_STRING * Link = &(Dx->Symlink); NextDevObj = NextDevObj->NextDevice; IoDeleteSymbolicLink(Link); IoDeleteDevice(Dx->Fdo); } } |
Скомпилируем и установим наш драйвер в систему (для этой цели я воспользовался удобной утилитой OSR Driver Loader). Теперь нам необходимо отослать драйверу IOCTL-запрос. Что ж, сделаем небольшую утилиту, которая нам в этом поможет:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include <Windows.h> #include <stdio.h> #define SYMLINK_NAME L"\\\\.\\SampleDriver" #define IOCTL_SAMPLE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) int main() { HANDLE DrvHandle = CreateFile ( SYMLINK_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ); if(DrvHandle == INVALID_HANDLE_VALUE) { printf("Can't open handle\n"); return -1; } UCHAR Buffer = 0xFF; DWORD Size; BOOL Status; while(TRUE) { Status = DeviceIoControl(DrvHandle, IOCTL_SAMPLE, &Buffer, sizeof(UCHAR), &Buffer, sizeof(UCHAR), &Size, NULL); if(Status == FALSE) { CloseHandle(DrvHandle); printf("IOCTL failed\n"); return -1; } printf("Driver returned: %d\n", Buffer); Sleep(1000); } return 0; } |
Компилируем и запускаем нашу утилиту, а также запускаем DebugView, чтобы видеть отладочный вывод драйвера.
Как мы видим, драйвер посчитал контрольные суммы интересующих нас секций и добавил в свой внутренний список. Теперь возьмем OllyDbg и изменим произвольный байт в секции кода. Драйвер незамедлительно сообщает о нарушении целостности секции и пишет об этом в логе:
Надеюсь, пример окажется кому-нибудь полезен.
Исходный код полностью: скачать
Кому нужна эта хунта
Отпишись от RSS, делов то.
Спасибо за пример.
Тоже хочу попробовать написать драйвер, который бы перехватывал обращение к определенным папкам и блокировал доступ. Но все руки никак не доходят.
Спасибо, прикольно, но думаю для
новичка сложновато будет.
))
Класс. Kaimi какой программой вы компилировали исходный код?
MSVC 2012
А как же быть с IAT и релоками ?
И что же с ними, если это "простой контроль" и цель - контролировать только секции с кодом в обычных исполняемых файлах?
То, что они в секциях кода и значения эти изменяются. Из за них кодосекции модуля на диске и в памяти различаются.
Так, продолжайте, и что дальше? Если мы читаем и проверяем исключительно исходя из того, что уже загружено в память.
Тогда как узнать что модуль в памяти изменён/пропатчен, сравнить ведь несчем ?
Например mov r,offset proc -- имеется релок для смещенья. Он может как угодно быть переписан.
IAT по отношенью к модулю на диске вообще содержит произвольные данные.
Сравнить с тем, что было при первом запросе на проверку. Это не способ защиты от хитрого хекера или чего-то подобного.
Каком есчо запросе ?
Вы скажите методу, которая позволит определить что образ изменён. А ваши фейковые проверки есмъ типичный аверский ход.
IOCTL-запросе. Софт делает запрос к драйверу, считается изначальная контрольная сумма, далее, при повторных запросах, контрольная сумма сверяется с изначальной.
А я в этом особо не разбираюсь.
Вы наверно не понимаете мою речь. Я спрашиваю КАК ОПРЕДЕЛЯЕТСЯ ЦЕЛОСТНОСТЬ ОБРАЗА ?
Причём тут ваши запросы в дров. Мой вредоносный код изменит образ как сказано выше, пофиксит IAT и как вы узнаете что он целый ?
Если IAT будет изменена до запуска процесса - никак, если IAT будет изменена до первого обращения к драйверу - никак.
IAT заполняется системным лодером при стартапе. Ежели вся суть чеканья в отправке запроса после запуска приложенья, то это не защита :)
Я в курсе. Если хотите - "минимальная защита" от возможного сбоя железа.
Был один метод, но дальше теории оно не прошло, возможно вы разрулите http://www.woodmann.com/forum/showthread.php?15158-detecting-or-preventing-patching-in-memory-(code-and-data)&p=94503&viewfull=1#post94503
Короче блог сей есмъ паганый холивар. Все вопросы снимаю!
Indy вы больной на всю голову человек, ищите себе без повода задачу или конфликт, но когда вам предлагают что-то решить, в личной обстановке, вы не беретёсь. Человек ясно написал, что решил попробовать написать драйвер, вместо Hello world, он не начал с того, что предлагает хитрую технику проверки кода на целостность.
Kaimi не слушайте этого идиота, нормальный у вас блог, интересно читать и пользы больше и большему кругу людей, чем от экскрементов Indy.
Вы друг мой явно не понимаете. Ежели человек начинает разрабатывать защиту, то он неизбежно становится авером, причём его не интересует дальнейшая проработка защиты. Именно здесь мы сие и наблюдаем. Дальнейшее сами обмозгуете, или быть может тоже помощь нужна ?
Шутить изволите? У меня даже образования в области IT нет, не говоря уже о полном отсутствии желания работать в ней.
У инде тоже его нет и желанья. Это не мешает обходить защиту, пилить морфы и дроверы :)
А аверы все с такого начинали. Далее будет защита на уровне сервисных таблиц етц, после чего рассмотрена возможность поработать в одной из ав контор(спермский обычно).
Далее мне будет лень, так как слишком сложно, долго и надо делать работу по фармакологии.
Так ты программирования сам освоил, расскажи как-?
Как... Пытался писать код, который делает то, что мне требовалось. Искал в гугле решения. Читал чужой код.
Indy вам мерещатся аверы по всюду. У человека хобби, он выкладывает для новичков информацию. Поэтому помощь тут нужна вам, сходите к психологу, он вам поможет разобраться в себе.
В себе мну разобрался. С сабжем не до конца.
Спасибо за код, но тут есть 2 недочёта
1. Если исполняемый файл содержит релоки, то при проецировании в память засчёт рандомизации адресного пространства секция кода должна будет отличатся от той что в файле (адреса релоков ведь загрузчик перепишет под текущее смещение)
2. Если исполняемый файл накрыт протектором или пакером, то его секции могут все иметь атрибуты RWE или RWEC. Т.е. алгоритм выборки секций работает только на стандартных скомпилиных файлах.
Если 2 этих недочёта поправить, то выйдет вполне рабочий вариант.
1. Так код ведь не оперирует с образом файла на диске, а исключительно с тем, что было уже загружено в память.
2. Есть такое дело.
Ага не досмотрел извеняюсь)
Сначала все было понятно, но по мере погружения в код я сбился и не смог разобраться.
пацан к успеху шел)