2016-12-30 23 views
7

Windows phải làm điều gì đó để phân tích cú pháp tiêu đề PE, tải tệp thực thi trong bộ nhớ và chuyển đối số dòng lệnh tới main().Windows Do Before Main() được gọi là gì?

dùng OllyDbg tôi đã thiết lập các chương trình gỡ rối để phá vỡ trên main() để tôi có thể xem các cuộc gọi stack: http://puu.sh/t5vxB/3d52089d22.png

Dường như nếu các biểu tượng được thiếu vì vậy chúng tôi không thể có được tên hàm, chỉ cần nó địa chỉ bộ nhớ như đã thấy. Tuy nhiên, chúng tôi có thể thấy người gọi chính là kernel32.767262C4, là mức độ ưu tiên của số ntdll.77A90FD9. Về phía dưới cùng của ngăn xếp, chúng ta thấy RETURN là ntdll.77A90FA4 mà tôi cho là hàm đầu tiên được gọi để chạy một tệp thực thi. Dường như các đối số đáng chú ý được truyền cho hàm đó là địa chỉ Trình xử lý ngoại lệ có cấu trúc của Windows và điểm vào của tệp thực thi.

Vậy chính xác các chức năng này kết thúc như thế nào khi tải chương trình vào bộ nhớ và chuẩn bị sẵn sàng cho điểm nhập thực thi? Trình gỡ lỗi có hiển thị toàn bộ quá trình được thực hiện bởi hệ điều hành trước main() không?

+1

Bộ nạp là chịu trách nhiệm cho việc này đọc. Nó là một thành phần của hệ điều hành, và cách nó hoạt động là như vậy mà bạn sẽ không thấy nhiều bằng chứng về nó trong ngăn xếp cuộc gọi. Nó là nếu không quá phức tạp để giải thích trong một câu trả lời Stack tràn. Xem xét mua một cuốn sách trên Windows Internals nếu bạn muốn tìm hiểu về ... tốt, các internals của Windows. :-) –

+0

Ngoài ra, Windows tải các tệp DLL cần thiết bởi EXE và thực hiện di chuyển. Và một số mã từ các DLL đó có thể thực thi trước main(). –

+1

liên quan: quá trình khởi động trên Linux: http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html. Ngoài ra [làm thế nào để tạo ra một thực thi nhỏ nhưng vẫn chạy được Linux] (http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html). Linux thực hiện hầu hết các công cụ khởi động quá trình trong hạt nhân so với Windows, nhưng một nhị phân động vẫn chạy liên kết động và sau đó là mã khởi động CRT trong ngữ cảnh của quy trình không gian người dùng mới được thực thi trước C 'main()' chức năng. –

Trả lời

7

nếu bạn gọi CreateProcess hệ thống nội bộ gọi ZwCreateThread[Ex] tạo chủ đề đầu tiên trong quá trình

khi bạn tạo chủ đề - bạn (nếu bạn trực tiếp gọi ZwCreateThread) hoặc hệ thống khởi tạo các bản ghi CONTEXT cho chủ đề mới - đây Eip(i386) hay Rip(amd64) sự điểm vào của chủ đề. nếu bạn làm điều này - bạn có thể chỉ định bất kỳ địa chỉ nào. nhưng khi bạn gọi điện thoại Create[Remote]Thread[Ex] - làm thế nào tôi nói - hệ thống điền CONTEXT và nó đặt tự thói quen là điểm nhập chủ đề. điểm vào ban đầu của bạn được lưu trong số Eax(i386) hoặc Rcx(amd64) đăng ký.

tên của thói quen này phụ thuộc từ phiên bản Windows.

đầu này là BaseThreadStartThunk hoặc BaseProcessStartThunk (trong trường hợp từ CreateProcess được gọi) từ kernel32.dll.

nhưng hiện tại hệ thống chỉ định RtlUserThreadStart từ ntdll.dll. số RtlUserThreadStart thường gọi BaseThreadInitThunk từ kernel32.dll (ngoại trừ các ứng dụng gốc (thực thi khởi động), như smss.exechkdsk.exe không có bất kỳ không gian địa chỉ nào). BaseThreadInitThunk đã gọi điểm nhập chuỗi ban đầu của bạn và sau (if) nó trở về - RtlExitUserThread được gọi.

enter image description here enter image description here mục tiêu chính của chủ đề chung khởi động wrapper này - thiết lập mức độ đầu lọc SEH. chỉ vì điều này chúng ta có thể gọi hàm SetUnhandledExceptionFilter. nếu chuỗi bắt đầu trực tiếp từ điểm vào của bạn, không có trình bao bọc - chức năng của Top level Exception Filter sẽ không khả dụng.

nhưng bất kỳ điểm nhập chuỗi - luồng nào trong không gian người dùng - KHÔNG BAO GIỜ bắt đầu thực hiện từ điểm này!

đầu khi chế độ người dùng chủ đề bắt đầu thực hiện - Hệ thống chèn APC cho chủ đề với LdrInitializeThunk như APC-thường - điều này được thực hiện bằng cách sao chép (tiết kiệm) chủ đề CONTEXT cho người sử dụng ngăn xếp và sau đó gọi KiUserApcDispatcher mà gọi LdrInitializeThunk.khi hoàn thành LdrInitializeThunk - chúng tôi quay lại KiUserApcDispatcher được gọi là NtContinue với chuỗi đã lưu CONTEXT - chỉ sau khi điểm nhập chủ đề đã bắt đầu được thực hiện.

nhưng bây giờ hệ thống thực hiện một số tối ưu hóa trong quy trình này - sao chép (lưu) chuỗi CONTEXT vào ngăn xếp người dùng và cuộc gọi trực tiếp LdrInitializeThunk. ở cuối hàm này NtContinue được gọi là - và điểm nhập chủ đề đang được thực thi.

vì vậy MỌI chủ đề bắt đầu thực hiện ở chế độ người dùng từ LdrInitializeThunk. (chức năng này với chính xác tên tồn tại và được gọi là trong tất cả các phiên bản Windows từ NT4 để win10)

enter image description here enter image description here gì là chức năng này làm gì? cho cái gì đây? bạn có thể nghe về thông báo DLL_THREAD_ATTACH? khi thread mới trong quá trình bắt đầu được thực thi (với ngoại lệ cho các luồng làm việc của hệ thống đặc biệt, như LdrpWorkCallback) - anh ta đi theo danh sách DLL được nạp và gọi các điểm nhập DLL với thông báo DLL_THREAD_ATTACH (tất nhiên nếu DLL có điểm vào và DisableThreadLibraryCalls không được gọi cho DLL này). nhưng làm thế nào điều này được thực hiện? nhờ LdrInitializeThunk mà gọi LdrpInitialize ->LdrpInitializeThread ->LdrpCallInitRoutine (đối với DLL EP)

enter image description here khi thread đầu tiên trong quá trình khởi động - đây là trường hợp đặc biệt. cần làm thêm nhiều công việc để khởi tạo quá trình. tại thời điểm này chỉ có hai mô-đun được tải trong quy trình - EXEntdll.dll. LdrInitializeThunk gọi LdrpInitializeProcess cho công việc này. nếu rất ngắn gọn:

  1. cấu trúc quá trình khác nhau được khởi
  2. tải tất cả các DLL (và người phụ thuộc của họ) mà EXE tĩnh liên kết - nhưng không gọi họ EPS!
  3. gọi LdrpDoDebuggerBreak - chức năng nhìn này - là debugger gắn liền với quá trình, và nếu có - int 3 gọi là - vì vậy debugger nhận được thông báo ngoại lệ - STATUS_BREAKPOINT - hầu hết gỡ rối có thể bắt đầu UI gỡ rối chỉ bắt đầu từ thời điểm này. Tuy nhiên tồn tại debugger (s) mà chúng ta hãy như quá trình gỡ lỗi từ LdrInitializeThunk - tất cả các ảnh chụp màn hình của tôi từ debugger loại này
  4. điểm quan trọng - cho đến khi trong quá trình thực thi mã chỉ từ ntdll.dll (và có thể từ kernel32.dll) - mã từ khác Các tệp DLL, bất kỳ mã của bên thứ ba nào chưa được thực thi trong quá trình.
  5. tùy chọn nạp shim dll để xử lý - Shim Engine khởi tạo. nhưng đây là BẮT BUỘC
  6. đi bộ bởi danh sách DLL được nạp và gọi EP với DLL_PROCESS_DETACH
  7. TLS khởi tạo và callbacks TLS gọi (nếu tồn tại)

  8. ZwTestAlert được gọi là - kiểm tra cuộc gọi này đang tồn tại APC trong thread xếp hàng và thực thi. điểm này tồn tại trong tất cả các phiên bản từ NT4 đến giành chiến thắng 10.này chúng ta hãy ví dụ như tạo ra quá trình trong lơ lửng trạng thái và sau đó chèn APC gọi (QueueUserAPC) để nó là thread (PROCESS_INFORMATION.hThread) - như kết quả cuộc gọi này sẽ được thực hiện sau khi quá trình sẽ được khởi tạo đầy đủ, tất cả DLL_PROCESS_DETACH gọi, nhưng trước khi EXE điểm vào. trong ngữ cảnh của quy trình xử lý đầu tiên.

  9. và NtContinue gọi cuối cùng - điều này khôi phục lại lưu chủ đề bối cảnh và chúng tôi cuối cùng đã nhảy cho chủ đề EP

enter image description here enter image description here cũng Flow of CreateProcess

+0

Câu hỏi có thể ngớ ngẩn: đây có phải là tất cả trước mã CRT được trình biên dịch đưa vào trong các tệp thực thi điển hình không? Trên Linux, điểm vào của tiến trình không gian người dùng thường được gọi là '_start' và được cung cấp bởi CRT. Chỉ có mã liên kết động chạy trước đó trong ngữ cảnh của quá trình không gian người dùng. Tôi ngạc nhiên không ai đã đề cập đến mã CRT như là một phần của những gì chạy trước hàm C 'main', vì đó là những gì mà tiêu đề câu hỏi yêu cầu (không chỉ về việc đạt được điểm vào CRT). Hoặc là một phần của công cụ này một phần của MSVCRT bình thường hoặc một cái gì đó? –

+0

@PeterCordes - tất cả những gì tôi đã viết ở tất cả không liên quan đến 'c/C++' 'CRT'. điều này đúng cho TẤT CẢ exe trong cửa sổ. tuy nhiên không phải tất cả các mã được viết trên 'c/C++' và thậm chí' c/C++' không thể sử dụng 'CRT'. nếu bạn sử dụng 'CRT' quá trình của bạn" enrty point "là wWinMainCRTStartup hoặc' wmainMainCRTStartup'. do đó, lưu lượng mã sẽ là 'LdrInitializeThink' ->' NtContinue' -> 'BaseThreadInitThunk' ->' wWinMainCRTStartup' -> 'WinMain'. tất cả điều này chỉ là cửa sổ liên quan. về Linux - tất cả những gì tôi biết rằng đây là hệ điều hành. không còn – RbMm

+0

@PeterCordes nữa - vì vậy tôi mô tả quy trình bên dưới/trước khi người dùng xác định EP cho quá trình hoặc chuỗi được gọi. nói rằng tôi không sử dụng 'CRT' - như là kết quả ở đây' người dùng định nghĩa EP' là chức năng của tôi (nó bất kỳ tên) - nó trực tiếp được gọi là từ 'BaseThreadInitThunk'. nếu bạn sử dụng 'CRT' -' người dùng xác định EP' của bạn là '[w] WinMainCRTStartup' hoặc' [w] mainMainCRTStartup' trong mã 'CRT', cuối cùng được gọi là' [w] WinMain' hoặc '[w] của bạn '(bạn phải sử dụng chính xác tên này, khi tôi tự do chọn tên) – RbMm

Các vấn đề liên quan