Qu’est-ce que Windows Do Before Main () est appelé?

Windows doit faire quelque chose pour parsingr l’en-tête PE, charger l’exécutable en mémoire et transmettre les arguments de ligne de commande à main() .

En utilisant OllyDbg, j’ai mis le débogueur à casser sur main () pour que je puisse voir la stack des appels: http://puu.sh/t5vxB/3d52089d22.png

Il semble que des symboles manquent pour que nous ne puissions pas obtenir le nom de la fonction, juste son adresse mémoire telle que vue. Cependant, nous pouvons voir que l’appelant de main est kernel32.767262C4 , qui est l’ ntdll.77A90FD9 de ntdll.77A90FD9 . En bas de la stack, nous voyons RETURN en ntdll.77A90FA4 que je suppose être la première fonction à être appelée pour exécuter un exécutable. Il semble que les arguments notables transmis à cette fonction soient l’adresse du gestionnaire des exceptions structurées de Windows et le point d’entrée de l’exécutable.

Alors, comment ces fonctions finissent-elles par charger le programme en mémoire et le préparer au point d’entrée? Est-ce que le débogueur montre tout le processus exécuté par le système d’exploitation avant main() ?

si vous appelez le système CreateProcess en interne, appelez ZwCreateThread[Ex] pour créer le premier thread en cours de traitement

Lorsque vous créez un thread – vous (si vous appelez directement ZwCreateThread ) ou le système, initialisez l’enregistrement CONTEXT pour le nouveau thread – ici Eip(i386) ou Rip(amd64) le point d’entrée du thread. Si vous faites cela, vous pouvez spécifier n’importe quelle adresse. mais quand vous appelez, dites Create[Remote]Thread[Ex] – comme je l’ai dit – le système remplit CONTEXT et définit l’auto-routine comme point d’entrée du thread. votre point d’entrée d’origine est enregistré dans le Eax(i386) ou Rcx(amd64) .

le nom de cette routine dépend de la version de Windows.

tôt c’était BaseThreadStartThunk ou BaseProcessStartThunk (dans le cas de CreateProcess appelé) de kernel32.dll .

mais maintenant, le système spécifie RtlUserThreadStart partir de ntdll.dll . RtlUserThreadStart appelle généralement BaseThreadInitThunk partir de kernel32.dll (à l’exception des applications natives (boot execute), telles que smss.exe et chkdsk.exe qui n’ont pas du tout kernel32.dll dans l’espace d’adressage). BaseThreadInitThunk appelle déjà votre point d’entrée de thread d’origine, et après (if) il renvoie – RtlExitUserThread appelé.

entrer la description de l'image ici entrer la description de l'image ici l’objective principal de cette enveloppe de démarrage commune des threads – définir le filtre SEH niveau supérieur. seulement parce que nous pouvons appeler la fonction SetUnhandledExceptionFilter . si le thread commence directement à partir de votre point d’entrée, sans wrapper – la fonctionnalité du filtre d’exception de niveau supérieur devient indisponible.

mais quel que soit le point d’entrée du thread – thread dans l’espace utilisateur – ne commencez jamais à exécuter à partir de là!

LdrInitializeThunk lorsque le thread du mode utilisateur commence à exécuter – le système insère APC pour LdrInitializeThunk avec LdrInitializeThunk comme Apc-routine – ceci est fait par copier (sauvegarder) le thread CONTEXT dans la stack utilisateur et ensuite appeler KiUserApcDispatcher qui appelle LdrInitializeThunk . lorsque LdrInitializeThunk terminé – nous revenons à KiUserApcDispatcher qui appelle NtContinue avec le thread enregistré CONTEXT – seulement après que le point d’entrée du thread commence à être exécuté.

mais maintenant le système effectue une optimisation dans ce processus – il copie (sauve) thread CONTEXT à la stack de l’utilisateur et appelle directement LdrInitializeThunk . à la fin de cette fonction, NtContinue s’appelle – et le point d’entrée du thread est en cours d’exécution.

Ainsi, chaque thread commence à s’exécuter en mode utilisateur à partir de LdrInitializeThunk . ( cette fonction avec le nom exact existe et appelée dans toutes les versions de Windows de nt4 à win10 )

entrer la description de l'image ici entrer la description de l'image ici qu’est-ce que cette fonction fait? car qu’est-ce que c’est? vous êtes peut-être à l’écoute de la notification DLL_THREAD_ATTACH ? quand le nouveau thread en cours commence à exécuter (avec exception pour les threads spéciaux du système, comme LdrpWorkCallback ) – il passe par une liste de DLL chargée et appelle les points d’entrée des DLL avec DLL_THREAD_ATTACH notification DLL_THREAD_ATTACH (bien sûr, si la DLL a un point d’entrée et DisableThreadLibraryCalls ). mais comment cela est-il mis en œuvre? grâce à LdrInitializeThunk qui appelle LdrpInitialize -> LdrpInitializeThread -> LdrpCallInitRoutine (pour les DLLs EP)

entrer la description de l'image ici lorsque le premier thread en cours de processus commence – c’est un cas particulier. besoin de faire beaucoup de travaux supplémentaires pour l’initialisation du processus. à ce moment, seuls deux modules chargés dans le processus – EXE et ntdll.dll . LdrInitializeThunk appelle LdrpInitializeProcess pour ce travail. si très brièvement:

  1. différentes structures de processus sont initialisées
  2. chargement de toutes les DLL (et leurs dépendants) auxquelles EXE est lié de manière statique – mais pas appelées EP!
  3. appelé LdrpDoDebuggerBreak – cette fonction regarde – le débogueur est associé au processus, et si yes- int 3 appelé – le débogueur reçoit donc un message d’exception – STATUS_BREAKPOINT – la plupart des débogueurs ne peuvent commencer le débogage qu’à partir de ce point. cependant, il existe des débogueurs qui permettent de déboguer à partir de LdrInitializeThunk – toutes mes captures d’écran de ce débogueur
  4. point important – jusqu’à ce que le processus soit exécuté uniquement à partir de ntdll.dll (et peut provenir de kernel32.dll ) – code provenant d’une autre DLL, tout code tiers non encore exécuté.
  5. cale dll chargée en option à traiter – le moteur Shim est initialisé. mais c’est facultatif
  6. marcher par DLL liste chargée et appeler ses EP avec DLL_PROCESS_DETACH
  7. Initialisations TLS et rappels TLS appelés (le cas échéant)

  8. ZwTestAlert est appelée – cette vérification d’appel existe APC dans la queue de thread et exécute son. ce point existe dans toutes les versions de NT4 pour gagner 10. ceci permet par exemple de créer un processus en état suspendu et d’insérer ensuite un appel APC ( QueueUserAPC ) à son thread ( PROCESS_INFORMATION.hThread ) – comme résultat cet appel sera exécuté après le processus entièrement initialisé, tout DLL_PROCESS_DETACH appelé, mais avant le point d’entrée EXE. dans le contexte du premier thread de processus.

  9. et NtContinue a finalement appelé – cette restauration a sauvegardé le contexte de thread et nous sums finalement passés à thread EP

entrer la description de l'image ici entrer la description de l'image ici lire aussi Flow of CreateProcess