Есть задача - заинжектить выполнение одного процесса в другой. Подробнее есть 2 exe x86: 1) C:\autoruns.exe - этот процесс стартует в засупенденном состоянии, в него пишется образ внедряемого екзе и потом ResumeThread (это прога autoruns из пакета sysinternals) , в нем через PE-редактор убран флажок Dll Can Move чтобы файл только работал по стандартной imagebase 0x400000 2) C:\1.exe - образ этого екзе инжекститься в autoruns.exe (простейший MessageBox ("hello world")) injector.exe - делает внедрение 1.exe в autoruns.exe Внедрение происходит стандартно, - собирается из rawbytes 1.exe его же виртуальный образ - запускается autoruns.exe в засуспенденном состоянии, для чистоты эксперимента на весь образ autoruns выставляются аттрибуты PAGE_EXECUTE_READWRITE - GetThreadContext замороженного потока autoruns - WriteProcessMemory в него записывается VirtualImage ("1.exe"), ImageBase у всех одинаковые 0x400000 , SizeOfImage 1.exe меньше autoruns.exe - context.eax = remoteImageBase + AddressOfEntryPoint("1.exe") - SetThreadContext(pi.hThread, &context ) - ResumeThread Код собсно проверенный и работает отлично на >= Windows7 , а вот на XP почему отказывается работать, autoruns даже не слетает, а после ResumeThread сразу завершается. Есть ли какие догадки почему такое происходит ? Вся сложность что к засупенденному процессу нельзя присоединить отладчик или бряк поставить, или можно как-то ? Можно ли через системные механизмы все-таки как-то брякнуться напр. на LdrpInitializeProcess или посмотреть лог - на каком этапе после ResumeThread процесс autoruns завершается ?
что интересно, вот после такого оно работает - подхватывает jit отладчик, x32dbg в данном случае , далее исправляю инструкцию на EP на оригинальную и передаю управление на нее - выскакивает MessageBox. проделывал такое как раз, это еще больше запутало меня на всяк прикладываю файлы https://fex.net/382610885309
[math][/math] расшифруйте... ап: попробовал выполнить 1.exe без импорта, просто цикл бесконечный - выполняется, значит чего-то импорт не настраивается.
Потому что при CREATE_SUSPENDED процесс замораживается не на OEP, а в середине процесса инициализации. В это время лоадер уже настроил импорты для kernel и ntdll, но не притрагивался к остальным. После замены образа и ResumeThread(), лоадер продолжит настройку импорта в новом образе, а импорты kernel32 в нем будут невалидны (потому что лоадер думает, что уже их настроил). Но это не точно
Или вот, что более вероятно: во внутренних структурах процесса лоадер сохраняет адрес точки входа из оригинального ехе и когда подменяем образ и размораживаем процесс, его выполнение начнется с фактически мусорного кода (старый OEP в новом образе).
неа, везде на всех ОС подгружена только одна ntdll при CREATE_SUSPENDED. Что интересно , на win10, server 2012, srv2016 - LdrpInitializeProcess также настраивает и релоки, на Win7 такая фишка не проходит - грузить только нужно по одинаковым imagebase, точнее процесс, в который внедряют, не должен релоцироваться или отсуствовать релоки или убран флажок dllcanmove. Т.е. только настройка импорта - собсно в сорцах LdrpInitializeProcess wrk там также нет настройки релоков. xp,vista - не пашет.
тоже думал над этим, но пока еще не пробовал - но что-то кажется маловероятным, в сорцах видно, что все пляшет от PE хидера, который находиться по адресу remoteImageBase, а там уже хидер нашего скопированного образа. И дальше уже заполняется Ldr исходя из нашего хидера Код (C++): Peb->Ldr = RtlAllocateHeap(Peb->ProcessHeap, MAKE_TAG( LDR_TAG ), sizeof(PEB_LDR_DATA)); NTSTATUS LdrpInitializeProcess ( IN PCONTEXT Context OPTIONAL, IN PVOID SystemDllBase, IN PUNICODE_STRING UnicodeImageName ) { PPEB Peb; NTSTATUS st; PWCH p, pp; UNICODE_STRING CurDir; UNICODE_STRING FullImageName; UNICODE_STRING CommandLine; HANDLE LinkHandle; WCHAR SystemDllPathBuffer[DOS_MAX_PATH_LENGTH]; UNICODE_STRING SystemDllPath; PLDR_DATA_TABLE_ENTRY LdrDataTableEntry; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; UNICODE_STRING Unicode; OBJECT_ATTRIBUTES Obja; BOOLEAN StaticCurDir = FALSE; ULONG i; PIMAGE_NT_HEADERS NtHeader = RtlImageNtHeader( NtCurrentPeb()->ImageBaseAddress ); // <= наш образ уже скопирован и читается в нового PE хидера PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData; ULONG ProcessHeapFlags; RTL_HEAP_PARAMETERS HeapParameters; NLSTABLEINFO InitTableInfo; LARGE_INTEGER LongTimeout; UNICODE_STRING NtSystemRoot; ... Peb->Ldr = RtlAllocateHeap(Peb->ProcessHeap, MAKE_TAG( LDR_TAG ), sizeof(PEB_LDR_DATA)); if ( !Peb->Ldr ) { RtlRaiseStatus(STATUS_NO_MEMORY); } Peb->Ldr->Length = sizeof(PEB_LDR_DATA); Peb->Ldr->Initialized = TRUE; Peb->Ldr->SsHandle = NULL; InitializeListHead(&Peb->Ldr->InLoadOrderModuleList); InitializeListHead(&Peb->Ldr->InMemoryOrderModuleList); InitializeListHead(&Peb->Ldr->InInitializationOrderModuleList); // // Allocate the first data table entry for the image. Since we // have already mapped this one, we need to do the allocation by hand. // Its characteristics identify it as not a Dll, but it is linked // into the table so that pc correlation searching doesn't have to // be special cased. // LdrDataTableEntry = LdrpImageEntry = LdrpAllocateDataTableEntry(Peb->ImageBaseAddress); LdrDataTableEntry->LoadCount = (USHORT)0xffff; LdrDataTableEntry->EntryPoint = LdrpFetchAddressOfEntryPoint(LdrDataTableEntry->DllBase); LdrDataTableEntry->FullDllName = FullImageName; LdrDataTableEntry->Flags = 0; // p = strrchr(FullImageName, '\\'); pp = UNICODE_NULL; p = FullImageName.Buffer; while (*p) { if (*p++ == (WCHAR)'\\') { pp = p; } } LdrDataTableEntry->FullDllName.Length = (USHORT)((ULONG)p - (ULONG)FullImageName.Buffer); LdrDataTableEntry->FullDllName.MaximumLength = LdrDataTableEntry->FullDllName.Length + (USHORT)sizeof(UNICODE_NULL); if (pp) { LdrDataTableEntry->BaseDllName.Length = (USHORT)((ULONG)p - (ULONG)pp); LdrDataTableEntry->BaseDllName.MaximumLength = LdrDataTableEntry->BaseDllName.Length + (USHORT)sizeof(UNICODE_NULL); LdrDataTableEntry->BaseDllName.Buffer = RtlAllocateHeap(Peb->ProcessHeap, MAKE_TAG( LDR_TAG ), LdrDataTableEntry->BaseDllName.MaximumLength ); RtlMoveMemory(LdrDataTableEntry->BaseDllName.Buffer, pp, LdrDataTableEntry->BaseDllName.MaximumLength ); } else { LdrDataTableEntry->BaseDllName = LdrDataTableEntry->FullDllName; } LdrpInsertMemoryTableEntry(LdrDataTableEntry); LdrDataTableEntry->Flags |= LDRP_ENTRY_PROCESSED; if (ShowSnaps) { DbgPrint( "LDR: NEW PROCESS\n" ); DbgPrint( " Image Path: %wZ (%wZ)\n", &LdrDataTableEntry->FullDllName, &LdrDataTableEntry->BaseDllName ); DbgPrint( " Current Directory: %wZ\n", &CurDir ); DbgPrint( " Search Path: %wZ\n", &LdrpDefaultPath ); } // // The process references the system DLL, so map this one next. Since // we have already mapped this one, we need to do the allocation by // hand. Since every application will be statically linked to the // system Dll, we'll keep the LoadCount initialized to 0. // LdrDataTableEntry = LdrpAllocateDataTableEntry(SystemDllBase); LdrDataTableEntry->Flags = (USHORT)LDRP_IMAGE_DLL; LdrDataTableEntry->EntryPoint = LdrpFetchAddressOfEntryPoint(LdrDataTableEntry->DllBase); LdrDataTableEntry->LoadCount = (USHORT)0xffff; LdrDataTableEntry->BaseDllName.Length = SystemDllPath.Length; RtlAppendUnicodeToString( &SystemDllPath, L"ntdll.dll" ); LdrDataTableEntry->BaseDllName.Length = SystemDllPath.Length - LdrDataTableEntry->BaseDllName.Length; LdrDataTableEntry->BaseDllName.MaximumLength = LdrDataTableEntry->BaseDllName.Length + sizeof( UNICODE_NULL ); ....
теперь для большей наглядности заинжектил вот такую прогу Код (C++): #include <Windows.h> int CALLBACK WinMain( _In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { int y = 0; int z = 0; while(TRUE) { y++; z++; } MessageBoxA(0,"qwer",0,0); } она зациклилась, присоединился отладчиком, перевел управление на MessageBox - все-таки импорты нормально настраиваются и я перешел по адресу user32!MessageBoxA. Но само окошко не высвечивается, далее начал переходить в недра messagebox, особо ничего толкового не заметил из-за чего не работает он. И все-таки подозрение на то, что нарушается окружение какое-то , или не дозаписывается в PEB чето, или как-то оно по пути файла ориентируется. Интересно, что такой вариант работает Код (C++): WCHAR toinjpath[MAX_PATH] = L"C:\\autoruns.exe"; DWORD injexesize = 0; BYTE* injexebytes = (BYTE*)_fileGetFileContents(L"C:\\12345678.exe", &injexesize); GetModuleFileNameW(0,toinjpath,256); // в самого себя инжектим в итоге ExecuteExeByInjection(injexebytes, toinjpath); А уже так - не пашет: Код (C++): WCHAR toinjpath[MAX_PATH] = L"C:\\autoruns.exe"; DWORD injexesize = 0; BYTE* injexebytes = (BYTE*)_fileGetFileContents(L"C:\\12345678.exe", &injexesize); //GetModuleFileNameW(0,toinjpath,256); //закоментил, внедряемся в C:\\autoruns.exe ExecuteExeByInjection(injexebytes, toinjpath); Чудеса просто, добавить нечего.
rmn, > Потому что при CREATE_SUSPENDED процесс замораживается не на OEP, а в середине процесса инициализации. При любом запуске треда начинается инициализация загрузчика, она проходит однократно. ntdll, kernel32, user32 либы не релокабельны. PedroPorosenko, > Можно ли через системные механизмы все-таки как-то брякнуться напр. на LdrpInitializeProcess или посмотреть лог Да. Загрузчик может выводить полный лог, для этого нужно запустить соотвествующий механизм, установив параметры в реестре. Так же загрузчик может накапливать стек вызовов, выполнять точки останова, аттачить отладчик. Поиск по форуму даст много инфы. В целом же впрыснуть экзешник можно только при остановленном процессе, который не начал исполнение. После того, как инициализация загрузчика закончилась, загрузить корректно экзе не получится(подключение к csrss фейлит, нужно много чего почистить в загрузчике). Может вы найдёте решение, но я его не вижу, поэтому и не использовался этот механизм. https://yadi.sk/d/a0n1noAB3Fa3pM
да, нашел уже механизмы, будем смотреть. будем копать, хочется докопаться до истины. спс, интересные сорцы.