В общем по умолчанию так делать конечно нельзя. Но я слышал, что эту проверку можно как-то обойти. Какие есть варианты? цель - усложнить процесс снятия хуков в kernel32 И ntdll. Потому что любой процесс может переписать мой kernel32 скопировав свой. Почему не драйвер - потому что на х64 куча проблем с патчгвардом и подписью драйверов.
float Это делается элементарно - создаётся секция(Odj), проецируется, в неё копируется по секциям все секции данных модуля, затем области кода восстанавливаются с диска, и поверх этого восстанавливаются секции данных из сохранённых в секции(Obj). Был один семпл(x86) для обнаружения модификаций кода посредством сравнения кода, описанного графом. Это старый семпл, уже давно на кряклатиносе и клабе был выложен(на клабе в силу низкого порога вхождения у "кодеров" началось негодование ). Суть вот в чём. Копия образа загружается средствами загрузчика - LdrLoadDll(). Так как нужно образ релоцировать(ибо модуль уже загружен), то необходимо загрузчик изменить. Разумеется не в памяти изменить. Он трассируется и выполняются необходимые манипуляции. Можно конечно вручную, но это очень, очень не тру, именно изза совместимости и множества нюансов. К чему такая релокация приводит - релоцируется код, при этом ссылки на секции данных не изменны. Этот принцип используется в межмодульном переключении тредов(описывать не буду, но если кратко то это своего рода патч, но без изменения оригинальной проекции ). http://rghost.ru/6076641
не очень понял. т.е. смысл в том чтобы PE заголовок остался на месте а изменились только местоположения секций?
float Нет. На месте остались переменные, тоесть секции данных(там на скрине это видно). Это даёт возможность сделать непосредственно джамп в восстановленную вторую проекцию. А её можно безопасно изменить. Например имеется последовательность вложенных процедур A-B-C-D. Какимто образом мы получаем управление в процедуре B. Нужно изменить процедуру D(тоесть внести в неё какието поправки). Она изменяется в копии проекции, а из полученного управления в процедуре B выполняется поправка: Ip = Ip + Delta, где Ip это Eip, Delta - дельта адресов двух проекций. После этого выполняется код второй проекции, переменные используются из первой проекции, детектор обнаруживает оригинальную проекцию не изменной. Если нужно получить управление при понижении NL(тоесть изменить нужно B, а управление получаем в D), то выполняется поправка всей SFC(или только определённого фрейма), тогда возврат будет выполнятся во вторую проекцию. Это аналогично переключению потока на отморфленный код, только без морфинга(это когда процедуры A целиком(также и B etc.) морфится - она пересобирается в буфере с необходимыми изменениями и поток переключается на этот код).
если я правильно понял, по настоящим адресам вместо функций будут джампы на новые адреса? если так то это по идее не спасет от полного восстановления кода из внешнего процесса. Если конкретнее - вот такой код, выполненный из внешнего процесса, разхучит мою LdrLoadDll DWORD l1 = (DWORD)GetProcAddress(kDLL, "LdrLoadDll"); VirtualProtectEx(hProcessToAttach,(LPVOID)l1,64,PAGE_EXECUTE_READWRITE,&op); WriteProcessMemory(hProcessToAttach,(LPVOID)l1,(LPCVOID)l1,64,&w); (скорее всего эта функция в защищаемом процессе будет на том же адресе, поэтому я и хочу как-то полностью сменить базу ntdll и kernel32) VirtualProtectEx(hProcessToAttach,(LPVOID)l1,64,op,&op);
float Заменить файловую проекцию на не файловую с атрибутом RE. Секции данных в проекции RW. Открыть доступ на запись в проекцию в таком случае не получится, а значит и код нельзя будет изменять. Ну из юзермода разумеется и без перемапа проекции.
Заменить Imag на Map? Я полагаю это делается так - копирую все что у нас в ntdll куда-нибудь в сторону, далее ZwUnmapViewOfSection на ntdll, далее ZwCreateSection с нужными правами, и ZwMapViewOfSection туда же, и восстанавливаем оригинальные байты?
float Переменные должны остаться неизменными. Восстанавливаются только кодесекции. "куда-нибудь" это в секцию(Obj). После этого она мапится по размеру файловых секций и с опред. атрибутами. Этот код давно написан, поиск по форуму.
я нашел кусок кода из утерянного ProtectImage от Клерка. В общем я как-то так и предполагал. С кернелом 32 это все ясно, а вот самая беда с ntdll. даже если загрузить второй ntdll в память, там при вызове функций будет проблема с этим кодом: 00D9D0B3 BA 0003FE7F MOV EDX,7FFE0300 00D9D0B8 FF12 CALL DWORD PTR DS:[EDX] 7FFE0300 - там должен быть записан адрес KiFastSystemCall но он размаплен. а записать адрес нового не выходит. и да, не очень понимаю как читается "это в секцию(Obj)."
float 0x7FFE0300 - данная переменная находится в USD, тоесть глобальна и туда загружается ссылка при инициализации ядра. Со стабами вообще не может быть проблем, так как их код базонезависимый. Чтобы не путать термины я указал в скобках что секция обьект(Section Object), а не файловая секция в модуле. Так как оба термина одинаковы, это вызывает путаницу, для этого введена поправка.
Аа ясно, я сразу не подумал. Вы наверно пытаетесь дёрнуть сервис из анмапленной проекции. Нужно шлюз у себя в коде разместить и через сискол или Int 0x2e дёргать.
gaeprust Так идея с контролем кода загрузчика это оказалось с латиноса. Что же вы так, без копирайтов то... Кодес с модулями без фиксапов дружит, кстати?
блин что-то не могу понять когда я создаю проекцию памяти на чтение и исполнение, как мне свой код то туда записать? Код (Text): HANDLE sec = 0; DWORD addr = 0; LARGE_INTEGER secoffset = {0}; SIZE_T viewsize = {0}; secoffset.LowPart = 0x1000; NtCreateSection(&sec,SECTION_MAP_EXECUTE|SECTION_MAP_READ,NULL, &secoffset,PAGE_EXECUTE_READ,SEC_COMMIT,NULL); NtMapViewOfSection(sec,NtCurrentProcess(),(PVOID *)&addr,NULL,NULL,NULL, &viewsize,ViewUnmap,NULL,PAGE_EXECUTE_READ); вот так он нормально мапит, но эти нули уже ничем не переписать получается
deLight О чём вы говорите ? По пунктам плз. float Есть такой обьект секция. Она мапится, на RW, туда данные записываются в проекцию, потом она анмапится, но данные хранятся в секции. Если её снова замапить, то данные отобразятся в память. Если её несколько раз замапить, то создаются общие PTE, тоесть в одной проекции данные меняются, они в другой также меняются, физическая страница то одна.
т.е. объект секции привязан к определенной физической странице? а можно ли как-то скопировать хендл секции в другой процесс и замапить там чтобы был такой же прикол с изменением данных?
Данные хранятся в секции. Блок данных из неё(тоесть по определённому смещению в секции) может быть отображён на произвольный адрес. Секция это обьект и обладает всеми свойствами, присущими обьектам, например может иметь описатель(хэндл). Можно скопировать описатель - NtDuplicateObject. Проецировать можно в чужой процесс, сервис получает в аргументах описатель процесса.
а как быть с гранулярностью? если я допустим создам секцию для пе хедера по адресу 7c800000 то на 7c801000 мне уже не даст замапить секцию. а у pe хедера должны быть права RW а за ним идет text у которого RE так что в 1 секции их не разместить. а wow64 и вообще когда я ему сказал замапить 1000 он замапил С0000 байт.
в смысле? по идее гранулярность у меня = 0х1000 байт. Секцию размером 0х1000 создать получается. А допустим после этой секции через VirtualAlloc можно будет выделить память? даже если можно, как быть с wow64? похоже придется куда-то все же сместить защищаемую секцию кода. Мне просто интересно как это было в PsImageProtect реализовано, потому что этого кода уже нигде не найти