Thứ Năm, 6 tháng 6, 2013

SEGMENT DEFINITION

Một 8086/8088 assembly language program file phải có phần mở rộng .asm

Có hai kiểu của các chương trình 8086/8088 assembly language: exe-format và com-format.

Một exe-format program sinh ra excutable files với mở rộng .exe. Một com-format program sinh excutable files với mở rộng .com.

Một exe-format program phải chứa một code segment và một stack segment. Nó có thể chứa data segment và một extra segment.

Một com-format chứa chỉ code segment.

Một programmer chọn một kích cỡ thích hợp cho stack segment, phụ thuộc vào kích thước chương trình của anh ta. Các giá trị nằm trong khoảng 100H đến 400H là hữu hiệu cho hầu hết các chương trình nhỏ.

Note: Trong một chương trình, data, code, stack segments có thể xuất hiện trong bất cứ thứ tự nào. Tuy nhiên, để tránh các tham chiếu về phía trước (forward references) tốt hơn là đặt data segment đứng trước code segment.


  • SIMPLIFIED SEGMENT DIRECTIVES 
MASM version 5.0 và các phiên bản phía trên, và TASM cung cấp một tập hợp đơn giản của directives dành cho việc khai báo segment được gọi là simplified segment directives. Để sử dụng các directives này, bạn phải khởi tạo một mô hình bộ nhớ (memory model), sử dụng .MODEL directive, trước khi khai báo bất cứ segment nào. Định dạng của .MODEL directive là:

                        .MODEL memory-model

memory model có thể là TINY, SMALL, MEDIUM, COMPACT, LARGE, HUGE hoặc FLAT:

memory model                                            description
TINY                          một segment. Do đó code và data cùng nhau không lớn hơn 64K
SMALL: một code segment. một data segment Do đó cả  code hoặc data không lớn hơn 64K
MEDIUM: nhiều hơn một code segment. một data segment. Do đó code có thể lớn hơn 64K
COMPACT: một code segment. nhiều hơn một data segment. Do đó data có thể lớn hơn 64K
LARGE: nhiều hơn một code segment. nhiều hơn một data segment. Không có mảng lớn hơn 64K. Do đó cả code và data có thể lớn hơn 64K
HUGE: nhiều hơn một code segment. nhiều hơn một data segment. Các mảng có thể lớn hơn 64K. Do đó cả code và data có thể lớn hơn 64K
FLAT: một segment lên tới 4GB. Tất cả data và code (bao gồm cả tài nguyên hệ thống) là các single 32-bit segment

Tất cả các mô hình chương trình ngoại trừ TINY dẫn đến sự tạo thành exe-format programs. TINY model tạo ra com-format programs. 

Mô hình bộ nhớ               Hệ điều hành                       Data và code được kết hợp
Tiny                                     MS-DOS                              Có
Small                                    MS-DOS, Windows              Không
Medium                                MS-DOS, Windows              Không
Compact                               MS-DOS, Windows              Không
Large                                    MS-DOS, Windows              Không
Huge                                    MS-DOS, Windows               Không
Flat                                       Windows NT                        Có

Simplified segment directives là : .CODE, .DATA, .STACK

.CODE directive có thể được thay sau bởi tên của code segment.

.STACK directive có thể được theo sau bởi kích thước của stack segment, bởi mặc địch kích thước là 1K, 1024 bytes

Định nghĩa segment mở rộng từ một simplified segment directive cho đến một simpified segment directive khác hoặc đến END directive nếu segment được định nghĩa là cái cuối cùng. 

  • Cấu trúc chung của một EXE-FORMAT PROGRAM
Ánh xạ bộ nhớ của một chương trình exe-format, với segments được định nghĩa theo thứ tự code, data, và stack:





ß SP
Stack segment


ß SS
Data segment



Code segment


ß CS , IP
PSP (100H bytes)


ß DS , ES





Các thanh ghi CS, IP được khởi tạo để trỏ đến bắt đầu của code segment.

Thanh ghi SS được khởi tạo để trỏ đến bắt đầu của stack segment.

SP được khởi tạo để trỏ đến 1 byte bên ngoài stack segment.

DS và ES được khởi tạo để trỏ đến bắt đầu của PSP (Program Segment Prefix) segment.

Đó là 100H (256) byte segment mà DOS tự động prefaces đến một chương trình khi chương trình đó được nạp vào trong bộ nhớ. 

PSP chứa thông tin quan trọng về chương trình

Do vậy, nếu một chương trình chứa một data segment, thanh ghi DS phải được khởi tạo bởi programmer để trỏ đến bắt đầu của data segment đó.

Tương tự nếu một chương trình chứa một extra segment, thanh ghi ES phải được khởi tạo bởi programmer để trỏ đến bắt đầu của extra segment đó.

Khởi tạo DS

Note: Các lệnh mà được khởi tạo thanh ghi DS cho một exe-format program với simplified segment directives là:

                            MOV  AX, @DATA
                            MOV  DS, AX

trong đó AX có thể được thay thế bởi bất cứ thanh ghi 16-bit mục đích chung nào khác.

Tại load time, @DATA được thay thế bằng 16-bit base address của data segment.

Do đó @DATA ước lượng đến một constant value; operand như vậy được gọi là immediate operand.

Bởi vì kiểu lệnh MOV:
           MOV  SegmentRegister, ImmediateOperand
là không hợp lệ, một dạng khởi tạo như:
           MOV  DS, @DATA
là không hợp lệ. Khởi tạo như vậy được hoàn thành một cách gián tiếp bởi việc sử dụng 16-bit general-purpose register. Ví dụ:
           MOV  AX, @DATA
           MOV  DS, AX

Note: Mọi 8086 assembly language program phải kết thúc với END directive. Directive này có thể được theo sau bởi một entry label, những gì báo cho assembler trỏ đến nơi thực thi chương trình bắt đầu. entry label có thể có bất cứ tên hợp lệ nào. 

Thứ Tư, 5 tháng 6, 2013

INSIDE THE NATIVE API

Mở đầu

Hầu như mọi người quen thuộc với NT đã từng nghe nói tới có một API ẩn được NT sử dụng nội bộ. API này, được gọi là Native API, nó hầu như được ẩn đi khỏi tầm nhìn, với chỉ một số ít trong các function của nó được documented để có thể được truy nhập công cộng. Việc che dấu này có thể dẫn tới một niềm tin rằng Native API có thể cung cấp các ứng dụng có năng lượng diệu kỳ, thậm chí có thể cho phép chúng bypass các biện pháp an ninh được implemented bởi các API chuẩn như Win32. Những suy nghĩ theo dòng này thường dẫn đến Native API conspiracy theory: Microsoft đang giữ API cho chính họ và các ứng dụng của họ để tạo ra lợi thế không cân bằng. Native API lộ ra một vài sắc thái không có sẵn thông qua các documented APIs (Ví dụ, bạn có thể xác định được các file đang mở có phân biệt chữ hoa, chữ thường, những thứ là không thể đối với Win32's CreatFile() hay OpenFile() ), tuy nhiên phần lớn khả năng của APIs là có thể truy nhập thông qua documented channels.

Bài viết này sẽ giới thiệu cho bạn về Native API và cung cấp cho bạn một lộ trình để tìm hiểu những gì có trong API này. Đầu tiên tôi sẽ mô tả Native API là gì, làm thế nào nó được yêu cầu trong các hoạt động bình thường, và làm thế nào sử dụng nó như là một cơ sở hỗ trợ cho các APIs của NT's operating environment subsystems. Sau đó tôi sẽ dẫn bạn vào một tour của API nơi tôi phân chia nó thành các tập hợp của các hàm liên quan (quản lý bộ nhớ, đồng bộ, etc.). Tôi sẽ nói về các khả năng sẵn có thông qua các hàm API và ghi chú Win32 APIs ánh xạ đến các Native APIs cụ thể nơi áp dụng. Một cái nhìn toàn diện về Native API giúp làm rõ các khái niệm sai lầm về cách nó được dùng, tại sao sử dụng nó, và những undocumented APIs đang được ẩn đối với chúng ta (như là có hay không consipary theory hợp lệ).

Native API Architecture 

Windows NT Native API phục vụ một mục đích: Như là một ý nghĩa cho việc gọi các dịch vụ hệ điều hành đặt tại kernel-mode trong một thói quen được điều khiển. Kernel-mode nơi nhân của NT thực thi và trong kernel các thành phần có thể truy nhập trực tiếp đến hardware và các dịch vụ thực hiện quản lý tài nguyên máy tính bao gồm bộ nhớ, các thiết bị và các tiến trình. Do vậy, bất cứ khi nào một chương trình thực thi trong user-mode muốn thực thi I/O, cấp phát hay giải phóng bộ nhớ ảo, bắt đầu một thread hoặc một process, hay là tương tác với các tài nguyên cục bộ. Nó phải gọi trên một hoặc nhiều dịch vụ "sống" tại kernel-mode.

Native API là tương tự như giao tiếp gọi hệ thống (system call interface) trên các hệ điều hành nguyên khối truyền thống ví dụ như họ UNIX. Tuy nhiên trên hầu hết các hệ điều hành Unix, system call interface được documented rất tốt và nó luôn sẵn sàng cho các ứng dụng chuẩn. Ví dụ, read() call thực hiện việc đọc dữ liệu từ một file, socket, hoặc một thiết bị đầu vào trong hầu hết các UNIX OS là một system call được thực hiện bởi code trong kernel-mode. Trong Windows NT Natvie API, system call interface của nó, được ẩn khỏi programmers đằng sau APIs cấp cao như Win32, OS/2, POSIX, hoặc DOS/WIN16. Nguyên nhân sự việc đó là do kiến trúc NT.

NT là một kiến trúc "modified microkernel ". Thay cho việc hỗ trợ một OS API cơ bản, NT implement một vài API. Việc đó được thực hiện hiệu quả bởi việc implementing operating environment subsystems  trong user mode để xuât khẩu các APIs cụ thể đến các chương trình người dùng. "national language" API của NT là Win32 và kiến trúc Win32 chứng minh khái niệm này. Win32 operating environment subsystem được phân chia giữa một server process , CSRSS.EXE (Client/Server Runtime Subsystem), và client-side DLLs mà được liên kết với các chương trình sử dụng Win32 API. Nhân của Win32 API được chia thành 3 loại: windowing và messaging, drawing và base services. Windows và messaging APIs bào gồm CreateWindow() và SendMessage() , và được export đến Win32 programs thông qua thư viện USER32.dll. BitBlt() LineTo() là Win32 drawing functions được cung cấp trong GDI32.dll. Cuối cùng, base services bao gồm tất cả Win32 I/O, process và thread, quản lý bộ nhớ, và các APIs đồng bộ và Kernel32.dll là thư viện export chúng.

Khi một Win32 program gọi một Win32 API điều khiển được chuyển từ trong không gian địa chỉ của nó vào một trong những Win32's client-side DLLs. DLL có thể thực thi một hay nhiều lựa chọn sau:

  • Lập tức quay lại caller
  • Gửi một message đến Win32 server để yêu cầu sự trợ giúp 
  • cầu khẩn Native APIs để thực hiện function
Lựa chọn thứ nhất hiếm khi xảy ra và chỉ có thể xảy ra khi DLL có thể phục vụ function mà không cần sự giúp đỡ của các dịch vụ hệ điều hành. Một ví dụ là GetCurrentProcess(). API này đơn giản trả lại một handle đến trạng thái hiện hành đã được cached trong KERNEL32 khi process bắt đầu. 

Lựa chọn thứ hai cũng hiếm khi được cần đến. một client-side DLL chỉ cần gửi messages đến Win32 server khi server phải cùng tham gia, và phải nhận thức được, thực thi của function. Win32 server tạo một Win32 execution environment cho client của nó mà liên quan đến duy tri một vài trạng thái liên quan đến client processes của nó. Do vậy, CreatProcess() API, exported bởi Win32, yêu cầu một tương tác với Win32 Server. Server trong trường hợp này chuẩn bị một process mới cho việc thực thi thông qua việc mapping trong một excutable image, tạo một command-line argument structure và tương tự. Win32 server gọi Native API functions để tạo process image thực sự và chuẩn bị không gian địa chỉ ánh xạ. 

Thứ Ba, 4 tháng 6, 2013

C++filt

Các ngôn ngữ cho phép function nạp chồng (overloading) phải có một cơ chế cho việc phân biệt giữa các phiên bản được nạp chồng (overfloaded versions) của một function bởi vì mỗi phiên bản có tên giống nhau. Ví dụ về C++ sau chỉ ra các prototypes cho một vài overloaded versions của một function có tên demo:

void demo(void);
void demo(int x);
void demo(double x);
void demo(int x, double y);
void demo(double x, int y);
void demo(char* str);

Theo luật chung, không thể có 2 functions với cùng một tên nằm trong một object file. Để cho phép overloading, compilers lấy các tên duy nhất cho overloaded functions bởi việc kết hợp thông tin mô tả một thứ tự các function arguments. Quá trình lấy các tên duy nhất cho các functions với các tên giống hệt nhau được gọi là name mangling. Nếu chúng ta sử dụng nm để dump các symbols từ một compiled version của đoạn code C++ phía trên. Chúng ta có thể nhìn thấy những thứ như sau :

idabook# g++ -o cpp_test cpp_test.cpp
idabook# nm cpp_test | grep demo
0804843c T _Z4demoPc
08048400 T _Z4demod
08048428 T _Z4demodi
080483fa T _Z4demoi
08048414 T _Z4demoid
080483f4 T _Z4demov

C++ không định nghĩa các chuẩn cho các cơ chế name-mangling, nó để cho compiler designers phát triển những cơ chế của họ. Để giải mã các mangled khác nhau của demo, chúng ta cần một công cụ hiểu được cơ chế name mangling của compiler của chúng ta (g++ trong trường hợp này). Đó là mục đích của c++flit . c++flit coi mỗi input word nếu nó là mangled name và sau đó cố gắng xác định compiler đã được sử dụng để sinh ra tên đó. Nếu tên xuất hiện là một mangled name hợp lệ, nó xuất ra demangled version của tên này. Khi c++flit không nhận dạng một word hay mangled name, nó đơn giản xuất ra word với ko có sự thay đổi nào.

Nếu pass kết quả của nm từ ví dụ trước vào c++flit, có khả năng khôi phục demangled function names, như ta thấy dưới đây:

idabook# nm cpp_test | grep demo | c++filt
0804843c T demo(char*)
08048400 T demo(double)
08048428 T demo(double, int)
080483fa T demo(int)
08048414 T demo(int, double)
080483f4 T demo()

Điều quan trọng cần lưu ý là mangled names chứa các thông tin bổ sung mà nm bình thường không cung cấp. Thông tin này có thể cực kỳ hữu ích trong khi ta reverse engineering, và trong nhiều trường hợp phức tạp khác, thông tin thêm này có thể bao gồm dữ liệu về class names hoặc function-calling conventions.

Liên kết tĩnh và liên kết động

Khi một excutable được tạo, vị trí của bất cứ hàm thư viện được tham chiếu bởi excutable đó phải được tìm ra. Linker có hai phương thức cho việc giải quyết các lời gọi tới các hàm thư viện: liên kết tĩnh liên kết động. Các đối số của command line cung cấp đến linker để xác định xem phương thức nào được sử dụng.

Một excutable có thể được liên kết tĩnh, liên kết động, hoặc cả hai.

Khi có một yêu cầu liên kết tĩnh, linker kết hợp application's object files với một bản sao của thư viện được yêu cầu để tạo một excutable file. Tại runtime, không cần locate library code vì nó đã được chứa bên trong executable. Các lợi thế của liên kết tĩnh là (1) nó đưa đến kết quả có các lời gọi hàm nhanh hơn và (2) việc phân bố của binaries là dễ dàng hơn bởi vì không cần những giả định về sự hiện diện của library code trên các hệ thống khách. Các mặt hạn chế của liên kết tĩnh bao gồm (1) excutables sinh ra có kích thước lớn và (2) khó khăn trong việc upgrading các chương trình khi các thành phần của thư viện thay đổi. Các chương trình thường gặp nhiều khó khăn để update bởi vì chúng phải  được tái liên kết (relinked) mỗi khi thư viện thay đổi. Trong hoàn cảnh reverse-engineering, liên kết tĩnh phức tạp một phần nào đó các vấn đề. Nếu chúng ta phải đối mặt với công việc phân tích binary được liên kết tĩnh, không có một cách dễ dàng để trả lời các câu hỏi "Các thư viện nào được liên kết trong binary này ?" và "Trong những function được liên kết thì function nào là library function? "

Liên kết động khác với liên kết tĩnh ở điểm linker không cần tạo ra bất cứ bản sao nào của các thư viện được yêu cầu. Thay vào đó, linker đơn giản chèn các tham chiếu đến bất cứ thư viện yêu cầu (thường là .so và .dll files) vào trong file thực thi cuối cùng, chúng ta sẽ có những excutable files nhỏ hơn. Upgrading library code là dễ dàng hơn rất nhiều khi liên kết động được tận dụng.  Bởi vì duy nhất một bản sao của library được duy trì và bản sao đó được tham chiếu đến nhiều binaries, thay thế một thư viện đã hết hạn duy nhất với một phiên bản mới ngay lập tức update tới mọi binaries sử dụng thư viện đó. Một trong những hạn chế của việc sử dụng liên kết động là nó yêu cầu quá trình loading phức tạp hơn. Tất cả các thư viện cần thiết phải được located và được load vào trong bộ nhớ, như đối lập với việc loading một file được liên kết tĩnh có một file chứa tất cả library code.

Một bất lợi khác của liên kết động đó là nhà sản xuất phải phân phối không chỉ các file thực thi của họ mà còn phải phân phối tất cả các file thư viện mà excutable phụ thuộc. Việc cố gắng thực thi một chương trình trên một hệ thống mà không chứa các file thư viện yêu cầu sẽ sinh ra lỗi.

Để cho việc liên kết tĩnh đến function thích hợp, binaries được liên kết động phải chỉ ra những thư viện nào chúng phụ thuộc đi cùng với resources xác định được yêu cầu từ mỗi thư viện.

Stripping Binary Excutable Files

Stripping một binary là quá trình loại bỏ các symbols từ binary file. Binary object chứa các symbols  như là kết quả của một quá trình biên dịch. Một vài trong những symbols này được tận dụng trong quá trình linking để giải quyết các tham chiếu giữa các files khi tạo file thực thi cuối cùng hay là binary. Trong các trường hợp khác, symbols có thể hiện diện để cung cấp các thông tin bổ sung cho việc sử dụng với debuggers. Theo sau quá trình linking, nhiều symbols không được yêu cầu. Options pass đến linker có thể giúp linker loại bỏ symbols không cần thiết tại build time. Việc sử dụng tiện ích strip có thể giúp loại bỏ symbols từ các binary files tồn tại. Trong khi một file đã được strip sẽ có kích thước nhỏ hơn file không được strip, việc cư sử của file đã được strip vẫn không thay đổi.

Device Drivers [Windows Internal]

Device drivers là loadable kernel-mode modules (thường kết thúc với đuối .sys) giao tiếp giữa I/O manager và phần cứng liên quan. Chúng run trong kernel mode trong một trong 3 bối cảnh sau:

  • Trong bối cảnh user thread bắt đầu một hàm I/O
  • Trong bối cảnh của một kernel-mode system thread
  • Như là một kết quả của một ngắt (và do đó không trong bối cảnh của bất cứ process cụ thể nào -- bất cứ process hay thread nào lưu hành khi ngắt xảy ra )

Device drivers không thao tác trực tiếp phần cứng, nhưng chúng gọi các hàm trong HAL để giao tiếp với phần cứng. Drivers thường được viết trong C (thỉnh thoảng là C++) và do đó, với việc sử dụng thích hợp các HAL routines, có thể là source code portable thông qua các kiến trúc CPU hỗ trợ bởi Windows và binary portable bên trong cùng một họ kiến trúc.

Có một vài kiểu device drivers:
  • Hardware device drivers (sử dụng HAL) write output hoặc lấy input từ các thiết bị vật lý hoặc mạng. Có nhiều kiểu hardware device drivers, ví dụ như bus drivers, human interface drivers, mass storage drivers, và tương tự.
  • File system drivers  là Windows drivers chấp nhận các requests I/O hướng file (file-oriented) và chuyển chúng vào trong I/O requests cho các thiết bị cụ thể.
  • File system filter drivers, những drivers kiểu này thực hiện giám sát đĩa (disk monitoring ) và mã hóa (encrytion), phân giải I/Os và thực hiện một vài quá trình thêm giá trị trước khi passing I/O đến tầng tiếp theo.
  • Network redirectors and servers là file system drivers thực hiện chuyển file system I/O requests đến một máy trên mạng và nhận các requests như vậy. 
  • Protocol drivers implement một giao thức mạng ví dụ như TCP/IP, NetBEUI, và IPX/SPX.
  • Kernel streaming filter drivers được xâu chuỗi cùng nhau để thực hiện xử lí tín hiệu trên các luồng dữ liệu, nhưu là recording hoặc displaying audio và video. 

Hardware Abstraction Layer [Windows Internal]

Một trong các thành phần quan trọng của thiết kế Windows là khả năng portable (portability) của nó qua các nền tảng phần cứng khác nhau. Hardware abstraction layer (HAL) là một phần "chìa khóa" trong việc tạo ra khả năng portable. HAL là một loadable kernel-mode module (Hall.dll) cung cấp một giao tiếp mức thấp (low-level interface) đến nền tảng phần cứng mà Windows đang chạy trên đó. Nó ẩn các chi tiết phụ thuộc phần cứng ví dụ như các giao tiếp xuất nhập (I/O interfaces), Các điều khiển ngắt (interrupt controller) và các cơ chế giao tiếp multiprocessor  -- bất cứ các hàm nào đó là phụ thuộc cả kiến trúc và phụ thuộc vào máy.

Vì vậy hơn là việc truy nhập phần cứng trực tiếp, các thành phần bên trong Windows cũng như các device drivers được viết bởi user duy trì khả năng portability bởi việc gọi HAL routines khi chúng cần thông tin phụ thuộc nền tảng. Vì lí do đó, HAL routines được documented trong Windows DDK. Để tìm hiểu thêm về HAL và ứng dụng của nó trong device drivers , tham khảo DDK.

Mặc dù một vài HAL có trong Windows, nhưng chỉ một cái được chọn tại lúc cài đặt và được sao chép tới system disk với filename Hall.dll.

Danh sách x86 HALs trong \Windows\Driver Cache\i386\Driver.cab

HAL File Name        Systems Supported
Hal.dll                       Standard PCs
halacpi.dll                  Advanced Configuration and Power Interface (ACPI) PCs
halapic.dll                  Advanced Programmable Interrupt Controller
halaacpi.dll                APIC ACPI PCs
Halmps.dll                 Multiprocessor PCs
Halmacpi.dll              Multiprocessor ACPI PCs
Halborg.dll          Silicon Graphics Workstation (Windows 2000 only; platform no longer marketed)
Halsp.dll                   Compaq SystemPro (Windows XP only)

Hardware support [Windows Internal]

Kernel trừu tượng hay là cô lập executive và device drivers ra khỏi sự khác nhau giữa các kiến trúc phần cứng được hỗ trợ bởi Windows. Công việc đó bao gồm việc xử lý các sự khác nhau trong các hàm như là xử lý ngắt, exception dispatching, và multiprocessor synchronization.

Thậm chí đối với các hàm liên quan đến hardware, việc thiết kế kernel cố gắng tối đa lượng code chung. Kernel hỗ trợ một tập hợp các giao tiếp, chúng portable và có ý nghĩa giống hệt nhau thông qua các kiến trúc. Hầu hết code để thực hiện giao tiếp portable đó cũng là giống hệt nhau qua các kiến trúc.

Một vài trong những interface kể trên được implement khác nhau trên các kiến trúc khác nhau, hoặc một vài trong những interfaces này được implement với code cho một kiến trúc xác định. Nhiều interfaces độc lập với kiến trúc có thể được gọi trên bất cứ máy nào, và ý nghĩa của các interfaces này sẽ là giống hoặc không giống code phụ thuộc vào kiến trúc. Một vài kernel interfaces (như là spinlock routines) được implement trong HAL bởi vì việc implement của chúng có thể khác nhau trên các hệ thống với cùng một họ kiến trúc.

Kernel còn chứa một lượng nhỏ code với các giao tiếp x86 để hỗ trợ cho các chương trình MS-DOS xưa. Các giao tiếp x86 không là portable, chúng không thể được gọi trên các máy dựa trên bất cứ kiến trúc khác, chúng sẽ không được hiện diện. Cho ví dụ, x86 code hỗ trợ các lời gọi để thao tác các bảng mô tả toàn cục (global descriptor tables) (GDTs) và LDTs, các đặc tính phần cứng của x86.

Các ví dụ khác của architecture-specific code trong kernel bao gồm interface để cung cấp translation buffer và CPU cache support. Việc support này yêu cầu code khác nhau cho các kiến trúc khác nhau bởi vì cách caches được implement.

Một ví dụ khác là context switching. Mặc dù tại high level các giải thuật giống nhau được sử dụng cho việc chọn thread và context switching (context của thread phía trước được lưu lại, context của thread mới được nạp vào, và thread mới bắt đầu), có các sự khác biệt liên quan đến kiến trúc giữa việc implementation trên các processors khác nhau. Bởi vì context được mô tả bởi trạng thài processor (các thanh ghi và tương tự), những gì được lưu và được nạp khác nhau phụ thuộc vào kiến trúc.

Thứ Bảy, 1 tháng 6, 2013

Định nghĩa dữ liệu trong Assembly

1. Các kiểu dữ liệu nội tại (Intrinsic Data Types)

MASM định nghĩa các kiểu dữ liệu nội tại, mỗi kiểu dữ liệu như vậy mô tả một tập hợp các giá trị có thể gán được cho các biến (variables) và các biểu thức (expressions) của một kiểu cho trước. Đặc tính quan trọng của mỗi kiểu là kích thước của nó tính theo bits: 8, 16, 32, 48, 64, và 80. Các đặc tính khác (như là dấu (signed), con trỏ(pointer), và dấu chấm động (floating-point) ) là tùy chọn và là chủ yếu cho lợi ích của programmers những người muốn được nhắc nhở về kiểu dữ liệu được tổ chức trong biến. Cho ví dụ, một biến được khai báo với như là DWORD, về logic thì giữ một giá trị nguyên 32-bit không dấu (unsigned 32-bit integer). Nhưng sự thực, nó có thể giữ một giá trị nguyên có dấu (signed 32-bit integer), một số thực 32-bit với độ chính xác đơn (32-bit single precision real) hoặc là là một con trỏ 32-bit (32-bit pointer). Assembler không phân biệt chữ hoa thường, vì vậy một directive ví dụ như là DWORD có thể được viết như là dword, Dword, dWORD và các trường hợp khác nữa.

Trong bảng 1, tất cả các kiểu dữ liệu dính dáng đến các số nguyên ngoại trừ 3 kiểu cuối cùng. Trong những kiểu trên, kí hiệu IEEE liên quan đến các định dạng chuẩn của số thực được công bố bởi IEEE Computer Society.

Bảng 1:
Kiểu
BYTE        :số nguyên 8-bit không dấu. B viết tắt cho Byte
SBYTE      :số nguyên 8-bit có dấu. S viết tắt cho Signed
WORD      : số nguyên 16-bit không dấu (còn có thể là con trỏ Near trong chế độ địa chỉ thực )
SWORD    : số nguyên 16-bit có dấu
DWORD   : số nguyên 32-bit không dấu (còn có thể là con trỏ Near trong chế độ địa chỉ thực). D viết tắt cho Double
SDWORD : số nguyên 32-bit có dấu. SD viết tắt cho Signed Double
FWORD    : số nguyên 48-bit không dấu ( con trỏ Far trong chế độ được bảo vệ)
QWORD   : số nguyên 64-bit. Q viết tắt của Quad
TBYTE     : số nguyên 80-bit (10-byte). T là viết tắt của Ten-byte
REAL4     : số thực 32-bit (4-byte) ngắn theo chuẩn IEEE
REAL8     : số thực 64-bit (8-byte) dài theo chuẩn IEEE
REAL10   : số thực 80-bit (10-byte) theo chuẩn IEEE

2. Các câu lệnh định nghĩa dữ liệu (Data Definition Statement)

một câu lệnh định dành một nơi lưu trữ trong bộ nhớ cho một biến, với một tên tùy chọn. Các câu lệnh định nghĩa dữ liệu tạo các biến dựa trên các kiểu dữ nội tại như trong bảng. Các định nghĩa dữ liệu tuân theo cú pháp sau:

    [tên] chỉ thị khởi tạo [, khởi tạo]...

Đây là một ví dụ của câu lệnh định nghĩa dữ liệu:
    count DWORD 12345

Tên: phải tuân theo các quy tắc trong assembly
chỉ thị (Directive) directive trong một câu lệnh định nghĩa dữ liệu có thể là BYTE, WORD, DWORD, SBYTE, SWORD hay bất cứ kiểu dữ liệu nào có trong bảng. Thêm vào đó, nó có thể có bất cứ các chỉ thị định nghĩa dữ liệu kế thừa được hiển thị trong bảng 2, cung cấp bởi Netwide Assembler (NASM) và Turbo Assembler (TASM) 

Bảng 2: các chỉ thị kế thừa

     DB : số nguyên 8 bit
     DW: số nguyên 16 bit
     DD: số nguyên 32 bit hoặc số thực
     DQ: số nguyên

Ntdll.dll

Ntdll.dll là một thư viện hỗ trợ hệ thống đặc biệt chủ yếu cho việc sử dụng subsystem DLLs. Nó chứa hai kiểu functions:

  • dịch vụ hệ thống gửi stubs đến Windows excutive system services
  • Các hàm hỗ trợ nội bộ (internal support functions) được sử dụng bởi subsystems, subsystem DLLs, và các native images khác.
Nhóm đầu tiên của functions cung cấp interface đến Windows excutive system services mà có thể được gọi từ user mode. Có hơn 200 hàm giống như vậy, ví dụ như NtCreateFile, NtSetEvent, và các hàm khác nữa. Hầu hết tính năng của các hàm như trên đều có thể được sử dụng thông qua Windows API. (Tuy nhiên, một số lượng hàm không thể truy nhập thông qua API, và chúng được sử dụng bên trong hệ điều hành)

Đối với những hàm kể trên (có thể truy nhập trong qua API), Ntdll chứa một entry point cùng tên với hàm đó. Code bên trong function chứa các lệnh đặc trưng kiến trúc để tạo ra một sự chuyển đổi vào trong kernel mode để yêu cầu dịch vụ điều vận hệ thống (system service dispatcher), dịch vụ điều vận này sau đó xác minh một vài tham số, gọi dịch vụ hệ thông thực sự trong kernel-mode chứa những mã thực (real code) bên trong Ntoskrnl.exe.

Ntdll còn chứa nhiều hàm hỗ trợ khác, ví dụ như image loader (functions bắt đầu với Ldr), heap manager, và Windows subsystem process communication functions (functions bắt đầu với Csr), cũng như là run-time library routines (functions bắt đầu với Rtl). Nó còn chứa user-mode asynchronous procedure call (APC) dispatcher và exception dispatcher.

Quản lý bộ nhớ x86

x86 processors quản lý bộ nhớ dựa theo các chế độ cơ bản của hoạt động.  Protected mode là mạnh mẽ và hiệu quả nhất, nhưng nó giới hạn các chương trình ứng dụng truy cập trực tiếp phần cứng hệ thống.

Trong chế độ địa chỉ thực (real address) , chỉ 1 MByte của bộ nhớ có thể được đánh địa chỉ, từ hexadecimal 00000 đến FFFFF. Processor chỉ có thể run một chương trình tại một thời điểm nhưng có thể ngắt trong chốc lát chương trình để xử lý các yêu cầu (được gọi là interrupts) từ các thiết bị ngoại vi. Các chương trình ứng dụng được phép truy nhập đến bất cứ vị trí bộ nhớ nào, bao gồm cả các địa chỉ được liên kết trực tiếp với phần cứng hệ thống. MS-DOS OS run trong real-address mode, và Windows 95 và 98 có thể được boot trong mode này.

Trong chế độ được bảo vệ (protected) , processor có thể run nhiều chương trình tại cùng một thời điểm. Nó phân cho mỗi process (chương trình đang chạy) tổng cộng 4GByte của bộ nhớ. Mỗi chương trình có thể được phân cho các vùng nhớ đã được dành riêng cho chúng và chương trình bị ngăn cấm đến code và data của các chương trình khác. MS-Windows và Linux run trong protected mode.

Trong chế độ virtual-8086 , máy tính run trong protected mode và tạo một máy ảo 8086 với chỉ 1-MByte không gian địa chỉ nó sở hữu để mô phỏng một máy tính 80x86 run trong real-address mode. Ví dụ, Windows NT và 2000, tạo một máy ảo 8086 khi bạn mở cửa sổ Command . Bạn có thể run nhiều cửa sổ như vậy tại cùng một thời điểm, mỗi cửa sổ được bảo vệ khỏi các hành động của các cửa sổ khác. Một vài chương trình MS-DOS tạo ra các tham chiếu trực tiếp đến phần cứng máy tính sẽ không run trong mode này dưới Windows NT, 2000 và XP.

Trong khuân khổ bài viết tôi xin trình bày chi tiết về Protected mode

Protected mode là một chế độ processor "bản địa" mạnh mẽ. Khi running trong protected mode, không gian địa chỉ tuyến tính của chương trình là 4GBytes, sử dụng số hexa đi từ 0 đến FFFFFFFF. Trong ngữ cảnh của Microsoft Assembler, mô hình phân chia phẳng (flat segmentation model ) là thích hợp cho lập trình protected mode. Mô hình phẳng là dễ sử dụng bởi vì nó yêu cầu chỉ một số nguyên 32-bit duy nhất để dữ địa chỉ của một lệnh hoặc biến. CPU thực hiện việc tính toán và dịch chuyển địa chỉ ở chế độ nền, tất cả chúng là trong suốt đối với người lập trình ứng dụng. Các thanh ghi phân đoạn (CS, DS, SS, ES, FS, GS) trỏ đến các bảng mô tả phân đoạn (segment descriptor tables), những bảng mà hệ điều hành sử dụng để theo dõi các vị trí của các phân đoạn riêng lẻ của chương trình. Một chương trình theo kiểu protected mode thường có 3 phân đoạn: code, data và stack, sử dụng các thanh ghi phân đoạn CS, DS SS:

  • CS tham chiếu bảng mô tả cho phân đoạn code
  • DS tham chiếu bảng mô tả cho phân đoạn dữ liệu
  • SS tham chiếu bảng mô tả cho phân đoạn stack
Mô hình phân đoạn phẳng

Trong mô hình phân đoạn phẳng, tất cả các phân đoạn được ánh xạ đến không gian địa chỉ vật lý 32-bit của máy tính. Có ít nhất hai phân đoạn được yêu cầu, một cho code và một cho data. Mỗi phân đoạn được định nghĩa bởi một mô tả phân đoạn (segment descriptor), một số nguyên 64-bit được lưu trong một bảng được biết đến như bảng mô tả toàn cục (global descriptor table (GDT)). Hình 1 hiển thị một mô tả phân đoạn có trường địa chỉ cơ sở (base address) trỏ đến vị trí biến đầu tiên trong bộ nhớ (00000000). Cũng trong hình này, giới hạn phân đoạn (segment limit) là 0040. Trường truy nhập (access) chứa các bits nhằm xác định cách segment được sử dụng. Tất cả các hệ điều hành hiện đại dựa trên kiến trúc x86 sử dụng mô hình phân đoạn phằng.