Dejavu Опубликовано 1 декабря, 2021 Поделиться Опубликовано 1 декабря, 2021 Привет, Пишу 32-битную DLL для мониторинга системных вызовов приложения, в которое она будет заинжекчена. Вот, собственно, код (не выдержка из проекта): Спойлер #include <Windows.h> static DWORD WINAPI Filter(DWORD magic, DWORD myclass, DWORD id, DWORD result, DWORD* args, DWORD argc) { //void* mem = malloc(10); //if (mem) free(mem); return 0; } BOOL WINAPI IsContainsFrame(DWORD ebp, unsigned int count) { DWORD lpEbp = ebp; DWORD lMax = __readfsdword(4); DWORD lMin = __readfsdword(8); DWORD _sig = 0x01020304; while (count && lpEbp < lMax && lpEbp > lMin) { if (*(DWORD*)(lpEbp + 8) == _sig) return true; count--; lpEbp = *(DWORD*)lpEbp; } return false; } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { LPVOID lpGate = (LPVOID)__readfsdword(0xc0); BYTE pushret[8] = "\x68\xDD\xCC\xBB\xAA\xC3\xCC"; BYTE asm_code[138] = "\x60\x6A\x10\x55\xBA\x33\x33\x33\x33\xFF\xD2\x85\xC0\x75\x77\x61\x55\x89\xE5\x60\x8B\x55\x04\x8B\x12\x80\xFA\xC3\x74\x5A\x80\xFA\xC2\x75\x59\xC1\xEA\x08\x81\xE2\xFF\x00\x00\x00\x52\x85\xD2\x74\x51\x8D\x4D\x0C\x51\x6A\xFF\x50\x68\x44\x44\x44\x44\x74\x0B\xFF\x74\x15\x08\x83\xEA\x04\x85\xD2\x75\xF5\x6A\x00\x68\x55\x55\x55\x55\x8B\x55\xF4\x8B\x4D\xF8\x90\x90\x90\x90\x90\x90\x90\x89\xEC\x60\x89\x45\xD4\x83\xEC\x14\xBA\x66\x66\x66\x66\x68\x04\x03\x02\x01\xFF\xD2\x61\x89\xE5\x5D\xC3\x31\xD2\xEB\xB0\x61\x89\xEC\x5D\xEB\xD5\x6A\x00\xEB\xAB\x61\xEB\xCE"; BYTE OriginalGateBytes[7]; memcpy_s(OriginalGateBytes, sizeof(OriginalGateBytes), lpGate, sizeof(OriginalGateBytes)); byte* lpMyAsmCode = (byte*)VirtualAlloc(0, sizeof(asm_code), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!lpMyAsmCode) break; memcpy_s(lpMyAsmCode, sizeof(asm_code), asm_code, sizeof(asm_code)); *(void**)(lpMyAsmCode + 0x5) = IsContainsFrame; *(void**)(lpMyAsmCode + 0x4d) = lpMyAsmCode + 0x5e; *(void**)(lpMyAsmCode + 0x68) = Filter; memcpy_s(lpMyAsmCode + 0x57, sizeof(OriginalGateBytes), OriginalGateBytes, sizeof(OriginalGateBytes)); *(void**)(pushret + 1) = lpMyAsmCode; MEMORY_BASIC_INFORMATION membasic; if (!VirtualQuery(lpGate, &membasic, sizeof(membasic))) break; if (membasic.AllocationProtect != PAGE_EXECUTE_READWRITE) { if (!VirtualProtect(membasic.BaseAddress, membasic.RegionSize, PAGE_EXECUTE_READWRITE, &membasic.Protect)) return false; memcpy_s(lpGate, sizeof(pushret), pushret, sizeof(pushret)); } break; } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } Вот код asm_code с моим беглым описанием: Спойлер pushad #сохраняем регистры push 0x10 #кол-во проверок ebp push ebp #пушим текущий ebp mov edx, 0x33333333 #кладем адрес IsContainsFrame call edx test eax, eax jnz magicDetected #если находим, то восстанавливаем регистры и выполняем без логирования popad #начало обработки push ebp #сохраняем текущий ebp. Он будет опорным элементом для удобной адресации по стеку mov ebp, esp pushad #сохраняем регистры mov edx, dword ptr [ebp + 4] #берем адрес возврата в Nt mov edx, [edx] #читаем оттуда dword, чтобы извлечь кол-во аргументов, которые принимает функция cmp dl, 0xC3 #если C3, значит аргументов нет jz zeroEdx cmp dl, 0xC2 #если C2, значит есть аргументы. Иначе просто выполняем без логирования jnz restoreAndExecuteJmp33 shr edx, 8 #двигаем направо, чтобы получить байт == кол-во аргументов and edx, 0xff #нужен только один байт. Сейчас тут находится кол-во аргументов функции Filter_SaveArgc: push edx #пуш кол-во аргументов test edx, edx jz zeroArgs #если аргументов нет, то и пушим 0 Filter_PushOther: lea ecx, dword ptr [ebp + 12] #иначе берем указатель на первый аргумент push ecx #и пушим его push 0xffffffff #пуш результат выполнения (пока что -1) push eax #пуш id вызова push 0x44444444 #пуш указателя на класс je FakeFrameHijack FakeFramePushArgs: push dword ptr [ebp + edx + 8] #пушим аргументы, с которыми была вызвана функция sub edx, 4 test edx, edx jne FakeFramePushArgs FakeFrameHijack: push 0 #не важно какой вызов был перед Nt push 0x55555555 #вызов моего кода, который будет адресом возврата из x64 (вместо Nt) mov edx, dword ptr [ebp - 12] #восстанавливаем старый edx mov ecx, dword ptr [ebp - 8] #восстанавливаем старый ecx jmp33: #здесь будут оригинальные байты прыжка nop nop nop nop nop nop nop AfterFakeFrameHandler: mov esp, ebp #возвращаемся к ebp, чтобы сохранить регистры pushad #сохраняем регистры mov dword ptr [ebp - 0x2c], eax #сохраняем результат вызова в фильтр sub esp, 0x14 #переходим к стеку с фильтром mov edx, 0x66666666 #адрес моей функции-фильтра push 0x01020304 #пушим magic call edx #вызываем фильтр popad #восстанавливаем сохраненные регистры mov ebp, esp #восстанавливаем старый ebp, с которым была вызвана фнукция pop ebp ret #возвращаем управление zeroEdx: xor edx, edx jmp Filter_SaveArgc restoreAndExecuteJmp33: popa mov esp, ebp pop ebp jmp jmp33 zeroArgs: push 0 jmp Filter_PushOther magicDetected: popad jmp jmp33 Когда происходит системный вызов, управление передается на мой asm_code (lpMyAsmCode). Предварительно функция IsContainsFrame() проверяет нет ли фрейма функции Filter (то есть не системный ли это вызов из моего фильтра, иначе не заходим снова в Filter, а просто выполняем). Далее на стеке наращиваю аргументы под функцию Filter, оставляю место для сохранения регистров (чтобы после всего этого регистры остались прежними) и копирую аргументы, с которыми вызывалась функция, подменяя адрес возврата. Делаю системный вызов (адресом возврата будет lpMyAsmCode + 0x5e), которая в последующем передаст управление. Проблема заключается в том, что я не могу обрабатывать получаемую информацию в Filter, так как любой вызов необходимой функции (malloc, sprintf_s) — краш (но не при первом вызове, по моим наблюдениям). В частности такая проблема с .NET приложениями. Я, конечно, грешу в том числе на метод инжекта — CreateProcess (CREATE_SUSPENDED) + CreateRemoteThread + LoadLibraryA. Но все же хочется услышать мнение знающих людей. Вот так примерно выглядит стэк вызовов (да, там моего кода не наблюдается, но он точно причастен!): Спойлер verifier!AVrfpDphPlaceOnUnusedList+8 1891000 cd22000 cd23000 cd22000 verifier!AVrfpDphAddNewPool+35 1891000 0001000 0000000 0000000 verifier!AVrfpDphAllocateNode+197 1891000 0000000 ca1ec7c ca1ec80 verifier!AVrfpDphFindAvailableMemory+12b 1891000 0002000 0000001 866a670 verifier!AVrfDebugPageHeapAllocate+185 1890000 1280002 0000010 261202c ntdll!RtlDebugAllocateHeap+39 0000010 26123f0 1890000 0000010 ntdll!RtlpAllocateHeap+f0 0000010 0000018 0000000 ca1ef48 ntdll!RtlpAllocateHeapInternal+104c 0280000 0000000 ca1f248 26122b4 ntdll!RtlAllocateHeap+3e 1890000 0280000 0000010 261389c ntdll!LdrpLoadDependentModule+bfa 731cf58 0000001 ca1f5ac ca1f5b0 ntdll!LdrpResolveForwarder+8a 1374f58 ca1f600 2613b6c 7e95d00 ntdll!LdrpSnapModule+50b 2613bc0 7e95d00 1368f74 7e95ce0 ntdll!LdrpProcessWork+4d 7da7210 c652f70 c68afb8 7da6d6d ntdll!LdrpWorkCallback+59 ca1f7fc 0000000 c652f70 c3a8ee0 ntdll!TppWorkpExecuteCallback+1dd ca1f7fc c652fe8 26135c4 7da5900 ntdll!TppWorkerThread+472 c3a8ee0 633fa10 ca1f93c 7dd7a9e kernel32!BaseThreadInitThunk+19 c3a8ee0 2613428 0000000 0000000 ntdll!__RtlUserThreadStart+2f fffffff 7df8a4f 0000000 0000000 ntdll!_RtlUserThreadStart+1b 7da5900 c3a8ee0 0000000 0000000 Ссылка на комментарий Поделиться на другие сайты Поделиться
Garik66 Опубликовано 2 декабря, 2021 Поделиться Опубликовано 2 декабря, 2021 В 01.12.2021 в 17:53, Dejavu сказал: Привет, Ай-я-яй? Ссылка на комментарий Поделиться на другие сайты Поделиться
Dejavu Опубликовано 2 декабря, 2021 Автор Поделиться Опубликовано 2 декабря, 2021 Видимо, никто не в курсе в чем может быть проблема. Закинул случайно ассемблерный код с ошибкой — неправильно обрабатывает системные вызовы, которые вызываются без аргументов. Решил избавиться от CRT-зависимых функций (malloc, sprintf_s) — перешел на функции в ntdll + не использую стандартную кучу, а создаю свою — вроде работает. Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения