2016-12-30 24 views
7

System Windows musi zrobić coś, aby przeanalizować nagłówek PE, załadować plik wykonywalny do pamięci i przekazać argumenty wiersza poleceń do main().Co wywołuje funkcja Windows Do Before Main()?

Korzystanie OllyDbg mam ustawić debugera do przerwy na main(), więc mogłem zobaczyć stos wywołań: http://puu.sh/t5vxB/3d52089d22.png

Wydaje się jakby symbole brakuje więc nie możemy dostać nazwę funkcji, tylko jej adres pamięci widoczny. Jednak widzimy, że głównym wywoływaczem jest kernel32.767262C4, który jest kłamcą ntdll.77A90FD9. W kierunku dołu stosu widzimy POWRÓT do ntdll.77A90FA4, który uważam za pierwszą funkcję, która kiedykolwiek zostanie wywołana, aby uruchomić plik wykonywalny. Wydaje się, że godnymi uwagi argumentami przekazywanymi do tej funkcji są: Windows Structure Exception Handler address oraz punkt wejścia pliku wykonywalnego.

Jak dokładnie te funkcje kończą się na załadowaniu programu do pamięci i przygotowaniu go do wykonania punktu wejścia? Czy debugger pokazuje cały proces wykonywany przez system operacyjny przed main()?

+1

ładowarka jest za to odpowiedzialny. Jest to składnik systemu operacyjnego, a sposób jego działania jest taki, że nie zobaczysz wielu dowodów na to w stosie wywołań. W innym przypadku wyjaśnienie w pojedynczej odpowiedzi z przepełnieniem stosu jest zbyt skomplikowane. Zastanów się nad zakupem książki na temat Windows Internals, jeśli chcesz dowiedzieć się o ... no cóż, o wewnętrznych cechach Windows. :-) –

+0

Ponadto, Windows ładuje biblioteki DLL wymagane przez EXE i przeprowadza relokację. A niektóre kody z tych bibliotek DLL mogą być wykonywane przed main(). –

+1

powiązane: proces uruchamiania w systemie Linux: http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html. Także [jak stworzyć malutki, ale wciąż działający plik wykonywalny Linuxa] (http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html). Linux robi większość rzeczy związanych z uruchamianiem procesu w jądrze niż Windows, ale dynamiczny plik binarny wciąż uruchamia dynamiczny linker, a następnie kod startowy CRT w kontekście nowo uruchomionego procesu przestrzeni użytkownika przed C 'main()' funkcjonować. –

Odpowiedz

7

jeśli zadzwonisz systemu CreateProcess wewnętrznie zadzwonić ZwCreateThread[Ex] stworzyć pierwszy wątek w procesie

podczas tworzenia wątku - ty (jeśli bezpośrednie połączenie ZwCreateThread) lub system zainicjować rekord nowego wątku CONTEXT - TUTAJ Eip(i386) lub Rip(amd64) na punkt wejścia wątku. jeśli to zrobisz - możesz podać dowolny adres. ale kiedy zadzwonisz, powiedz Create[Remote]Thread[Ex] - jak mówię - wypełnij system CONTEXT i ustaw samoczynną rutynę jako punkt wejścia wątku. Twój oryginalny punkt wejścia zostanie zapisany w rejestrze Eax(i386) lub Rcx(amd64).

Nazwa tej procedury zależała od wersji systemu Windows.

wcześniej to było BaseThreadStartThunk lub BaseProcessStartThunk (w przypadku od CreateProcess nazywany) od kernel32.dll.

, ale teraz system określa RtlUserThreadStart z ntdll.dll. RtlUserThreadStart zwykle wywołuje BaseThreadInitThunk od kernel32.dll (z wyjątkiem rodzimych (uruchamianie rozruchu) aplikacji, takich jak smss.exe i chkdsk.exe, które nie mają w ogóle przestrzeni adresowej w ogóle). BaseThreadInitThunk już wywołuje swój pierwotny punkt wejścia wątku i po (jeśli) go zwróci - RtlExitUserThread wywołana.

enter image description here enter image description here głównym celem tego wspólnego wątku startowego owijki - zestaw najwyższym poziomie SEH filtr. tylko dlatego, że możemy nazwać funkcję SetUnhandledExceptionFilter. jeśli wątek rozpoczyna się bezpośrednio od punktu wejścia, bez opakowania - funkcja Top level Exception Filter staje się niedostępna.

ale niezależnie od punktu wejścia wątku - wątku w przestrzeni użytkownika - NIGDY zacznij wykonywać od tego punktu!

wcześnie, kiedy użytkownik rozpoczyna wątek Tryb wykonać - System APC wstawić do wątku z LdrInitializeThunk jako APC-rutyna - odbywa się to poprzez kopiowanie (zapisz) wątek CONTEXT do stosu użytkownika, a następnie zadzwonić KiUserApcDispatcher który nazywamy LdrInitializeThunk.gdy zakończy się LdrInitializeThunk - wracamy do KiUserApcDispatcher, który wywołał NtContinue z zapisanym wątkiem CONTEXT - dopiero po tym, jak już zaczyna się wykonywanie punktu wejścia wątku.

, ale teraz system wykonuje pewne optymalizacje w tym procesie - kopiuje (zapisuje) wątek CONTEXT do stosu użytkownika i wywołuje bezpośrednio LdrInitializeThunk. na końcu tej funkcji wywołana - i wykonywany jest punkt wejścia wątku.

tak KAŻDY wątek rozpocząć wykonywać w trybie użytkownika z LdrInitializeThunk. (tej funkcji z dokładnie nazwie istnieje i nazywa się we wszystkich wersjach Windows z NT4 do win10)

enter image description here enter image description here Co to jest funkcja zrobić? bo co to jest? możesz być słuchać powiadomienia o DLL_THREAD_ATTACH? kiedy rozpoczyna się nowy wątek w procesie (z wyjątkiem specjalnych obrobionych przez system wątków, takich jak LdrpWorkCallback) - przechodzi on przez załadowaną listę DLL i wywołuje punkty wejściowe bibliotek DLL z powiadomieniem DLL_THREAD_ATTACH (oczywiście, jeśli DLL ma punkt wejścia i DisableThreadLibraryCalls nie jest wywoływany dla tej biblioteki DLL). ale w jaki sposób jest to realizowane? Dzięki LdrInitializeThunk, które wymagają LdrpInitialize ->LdrpInitializeThread ->LdrpCallInitRoutine (DLL dla PE)

enter image description here gdy pierwsza nić na początku procesu - jest to przypadek szczególny. potrzeba wielu dodatkowych zadań do inicjalizacji procesu. w tej chwili tylko dwa moduły załadowane w procesie - EXE i ntdll.dll. LdrInitializeThunk zadzwoń pod numer LdrpInitializeProcess dla tego zadania. jeśli bardzo krótko:

  1. różne struktury proces jest inicjowany
  2. ładowanie wszystkich DLL (i ich rodzin), do którego EXE statycznie powiązane - ale nie nazywają EPS!
  3. nazywa LdrpDoDebuggerBreak - funkcja wygląd - są debugger dołączone do procesu, a jeśli tak - int 3 nazywa - tak debugger otrzymać wiadomość Wyjątek - STATUS_BREAKPOINT - większość debugery mogą rozpocząć debugowanie rozpocząć UI tylko z tego punktu. jednak istnieje debugger (S), które pozwoli jak proces debugowania z LdrInitializeThunk - wszystkie moje screeny z tego rodzaju debuggera
  4. ważny punkt - aż w procesie wykonywany kod tylko z ntdll.dll (i może wynosić od kernel32.dll) - kod z innego Pliki DLL, jakikolwiek kod firm zewnętrznych, który nie został jeszcze wykonany w procesie.
  5. Opcjonalny załadowany shim dll do przetworzenia - Zainicjowano silnik Shim. ale jest to opcjonalne
  6. spacer przez załadowanej listy DLL i wywołać jego EPki z DLL_PROCESS_DETACH
  7. TLS inicjacji i wywołania zwrotne TLS zwanych (jeśli istnieje)

  8. ZwTestAlert nazywa - to sprawdzenie połączenia są istnieć APC w wątek kolejki i wykonaj jego. ten punkt istnieje we wszystkich wersjach od NT4 do win 10.To pozwoli na przykład stworzyć proces w zawiesinie w a następnie wstawić połączenia APC (QueueUserAPC), aby gwint (PROCESS_INFORMATION.hThread) - w wyniku tego połączenia będzie wykonywane po zakończeniu procesu zostanie całkowicie inicjowane wszystkie DLL_PROCESS_DETACH nazywa, ale przed EXE Punkt wejścia. w kontekście pierwszego wątku procesu.

  9. i NtContinue nazywa wreszcie - to przywrócenie zachowanego kontekstu wątek i wreszcie przejść do wątku EP

enter image description here enter image description here przeczytać również Flow of CreateProcess

+0

Prawdopodobnie głupie pytanie: czy to wszystko przed kodem CRT, który jest dołączony przez kompilator w typowych uruchomieniach plików wykonywalnych? W systemie Linux punkt wejścia procesu użytkownika jest zwykle nazywany "_start" i jest dostarczany przez CRT. Tylko dynamiczny kod linkera działa wcześniej w kontekście procesu przestrzeni użytkownika. Jestem zaskoczony, że nikt nie wspomniał o kodzie CRT jako części tego, co działa przed funkcją C 'main', ponieważ tak właśnie pyta tytuł pytania (nie tylko o osiągnięcie punktu wejścia CRT). A może niektóre z tych rzeczy są częścią normalnego MSVCRT czy coś takiego? –

+0

@PeterCordes - wszystko co napisałem w ogóle nie związane z 'c/C++' 'CRT'. odnosi się to do WSZYSTKIEGO exe w oknach. jednak nie cały kod zapisany na 'c/C++' i nawet 'c/C++' nie może w ogóle używać 'CRT'. jeśli używasz 'CRT', twój proces" enrty point "to wWinMainCRTStartup lub' wmainMainCRTStartup'. więc przepływem kodu będzie 'LdrInitializeThink' ->' NtContinue' -> 'BaseThreadInitThunk' ->' wWinMainCRTStartup' -> 'WinMain'. wszystko to dotyczy tylko okien. o Linuksie - wszystko, co wiem, że to system operacyjny. nie więcej niż – RbMm

+0

@PeterCordes - tak opisuję proces poniżej/przed wywołanym przez użytkownika EP dla procesu lub wątku. powiedzmy, że nie używam 'CRT' - w wyniku tutaj' zdefiniowana przez użytkownika EP' to moja funkcja (to dowolna nazwa) - jest bezpośrednio wywoływana z 'BaseThreadInitThunk'. jeśli używasz 'CRT' - twoja' zdefiniowana przez użytkownika EP' to '[w] WinMainCRTStartup' lub' [w] mainMainCRTStartup' w kodzie 'CRT', która już w końcu nazywała twoją' [w] WinMain' lub '[w] główną '(musisz użyć dokładnie tych nazw, gdy wybieram dowolną nazwę) – RbMm