Đăng ký Đăng nhập

Tài liệu Chuỗi định dạng

.PDF
27
343
55

Mô tả:

Chuỗi định dạng ● Hàm printf() có dạng ● ● Nếu gọi printf(“Hello”); ● ● Hello Nếu gọi printf(“1%”); ● ● printf(const char *format, …) 1% Nếu gọi printf(“1%%”); ● 1% Chuỗi định dạng ● ● ● → Dấu % có ý nghĩa đặc biệt % đánh dấu sự bắt đầu của một yêu cầu định dạng Yêu cầu định dạng tận cùng bởi ký tự định dạng Chuỗi định dạng ● % in ra ký tự % ● c in tham số thứ nhất như một ký tự ● x in tham số thứ nhất ở dạng thập lục ● X in tham số thứ nhất ở dạng THẬP LỤC ● s in chuỗi được chỉ tới bởi tham số thứ nhất ● ● n ghi vào ô nhớ có địa chỉ xác định bởi tham số thứ nhất số lượng ký tự đã in, 4 byte hn giống n nhưng chỉ ghi 2 byte Chuỗi định dạng ● printf(“%c %X”, 0x87654321, 0x12345678) ● ● printf(“%x %X”, 0x6789ABCD, 0x6789ABCD) ● ● ! 12345678 6789abcd 6789ABCD printf(“%s”, “1%%”) ● 1%% Chuỗi định dạng ● int cookie = 0 printf(“12345678%n”, &cookie) ● ● 12345678 int cookie = 0 printf(“1234%n5678”, &cookie) ● 12345678 ● Sau lệnh printf() đầu tiên, cookie = 8 ● Sau lệnh printf() thứ hai, cookie = 4 Chuỗi định dạng ● Xét ví dụ: ● ● int main() { char buffer[512]; int cookie = 0; gets(buffer); printf(buffer); } Chương trình nhận một chuỗi qua gets(), và truyền chính chuỗi đó làm tham số thứ nhất của printf() Chuỗi định dạng ● Nhập vào abcdef ● ● Nhập vào %x ● ● abcdef 0 Nhập vào %x %x %x %x ● 0 0 0 6 Chuỗi định dạng ● Xét trường hợp ● ● printf(“%x %x %x %x”, 1, 2, 3, 4); PUSH PUSH PUSH PUSH PUSH CALL 4 3 2 1 format printf Chuỗi định dạng ● Xét trường hợp ● ● printf(“%x %x %x %x”); PUSH CALL format printf Chuỗi định dạng Chuỗi định dạng ● Nhập vào %x %x %x %x %x %x %x %x %x %x %x %x ● ● 0 0 0 6 b7ead8e0 fffff 51 0 0 25207825 78252078 20782520 Chúng ta gặp lại dữ liệu nhập → Có thể kiểm soát tham số của hàm printf() ● Dữ liệu nhập bắt đầu từ tham số thứ 10 ● Giả sử địa chỉ cookie là BFFFF854 Chuỗi định dạng Chuỗi định dạng ● Để gán 0x64 vào cookie ● ● ● ● ● [địa chỉ cookie]%x%x%x%x%x%x%x%x%x[...]%n [địa chỉ cookie] in ra 4 byte \x54\xF8\xFF\xBF 9 %x in ra 21 byte 0006b7ead8e0fffff5100 Để in ra tổng cộng 100 ký tự ta cần thêm 100 – 21 – 4 = 75 ký tự Vậy […] sẽ là 75 ký tự Chuỗi định dạng ● Để gán 0x100 vào cookie ● ● ● ● ● [địa chỉ cookie]%x%x%x%x%x%x%x%x%x[...]%n [địa chỉ cookie] in ra 4 byte \x54\xF8\xFF\xBF 9 %x in ra 21 byte 0006b7ead8e0fffff5100 Để in ra tổng cộng 256 ký tự ta cần thêm 256 – 21 – 4 = 231 ký tự Vậy […] sẽ là 231 ký tự Chuỗi định dạng ● Để gán 0x300 vào cookie ● ● ● ● ● [địa chỉ cookie]%x%x%x%x%x%x%x%x%x[...]%n [địa chỉ cookie] in ra 4 byte \x54\xF8\xFF\xBF 9 %x in ra 21 byte 0006b7ead8e0fffff5100 Để in ra tổng cộng 0x300 ký tự ta cần thêm 0x300 – 21 – 4 = ... ký tự Vậy […] sẽ là ... ký tự → tràn biến buffer Chuỗi định dạng ● ● Để gán 0x300 vào cookie, ta phải nhập vào ít hơn, nhưng vẫn đảm bảo in ra đủ 0x300 ký tự Tùy chọn độ dài tối thiểu là một số nguyên dương, không bắt đầu bằng số 0, nằm giữa dấu % và ký tự định dạng ● $10x → in số thập lục rộng tối thiểu 10 ký tự ● $2x → in số thập lục rộng tối thiểu 2 ký tự ● ● lớn hơn 0xFF → cần nhiều hơn 2 ký tự → 0 kiểm soát được → luôn sử dụng tối thiểu là độ rộng tối đa cần thiết (ví dụ $8x vì không số nào cần nhiều hơn 8 ký tự) Chuỗi định dạng ● 4 byte địa chỉ ● 8 yêu cầu %8x ● 0x300 – 4 – 4 * 8 = 700 → yêu cầu %700x ● Và yêu cầu %n ● Tổng số ký tự nhập vào là 4 + 8 * 3 + 5 + 2 = 23 ký tự Chuỗi định dạng ● ● ● ● ● 4 byte địa chỉ và 1 yêu cầu %764x đã in ra đủ 0x300 ký tự Làm sao để ép yêu cầu %n nhận tham số thứ 10 thay vì tham số thứ 2 Tùy chọn vị trí tham số là một số nguyên dương đi ngay sau ký tự % và kết thúc bằng ký tự $ %10$n sẽ ép yêu cầu %n sử dụng tham số thứ 10 %24$8x sẽ in giá trị của tham số thứ 24 ở dạng thập lục với độ rộng tối thiểu là 8 ký tự Chuỗi định dạng ● Để cookie = 0x87654321 ● Không thể in hơn 2 tỷ ký tự! ● ● Nhận xét rằng 0x87654321 gồm 4 byte 21, 43, 65, 87 Thay vì ghi một lần, chúng ta có thể ghi tuần tự 4 lần ● 4 lần ghi → 4 địa chỉ → đã in 0x10 ký tự ● Số đầu là 0x21 → cần thêm 0x11 ký tự đệm ● Số hai là 0x43 → cần thêm 0x22 ký tự đệm ● ... Chuỗi định dạng ● Để cookie = 0x12345678 ● Hai câu hỏi: ● ● 78, 56, 34, 12 → ghi 12 trước, 34 sau...? ● Làm sao để từ 78 thành 56? Khi ghi vào bộ nhớ, 4 byte sẽ được ghi cùng một lúc → lần ghi sau sẽ đè lên lần ghi trước ● ● → nếu ghi ở địa chỉ cao trước thì khi ghi ở địa chỉ thấp sẽ đè lên giá trị đã ghi ở địa chỉ cao → vì lần ghi sau ở địa chỉ cao đè lên lần ghi trước ở địa chỉ thấp hơn nên chúng ta chỉ quan tâm đến các byte thấp → 0x156 hay 0x256 đều đem lại 0x56 Chuỗi định dạng Chuỗi định dạng ● Có nhiều cách cắt ● ● ● 2-2 hn-hn (không lem) 1-1-2 n-n-hn hoặc n-hn-hn hoặc hn-hn-hn... (có thể bị lem) 1-1-1-1 n-n-n-n (lem ít nhất 1 byte) Chuỗi định dạng Chuỗi định dạng ● Để cookie = 0x04030201 ● 01 02 03 04 ● → lần đầu ghi 0x101 ● → chèn một ký tự bất kỳ, ghi lần 2 ● → chèn một ký tự bất kỳ, ghi lần 3 ● → chèn một ký tự bất kỳ, ghi lần 4 ● → [16 byte địa chỉ...]%241x%10na%11nb %12nc%13n Chuỗi định dạng ● Khi chuỗi nhập bắt đầu bằng “UIT” ● Nhận xét: ● ● ● ● ● … “UIT” chiếm mất tham số thứ 10 “UIT” (3 byte) chưa đủ lấp tham số thứ 10 → cần thêm 1 byte bất kỳ Vì tham số thứ 10 đã bị chiếm nên các lần ghi sẽ phải sử dụng tham số 11, 12, 13, và 14 Vì có 4 ký tự chèn vào trước, nên cần tính toán lại các đoạn đệm Chuỗi định dạng Chuỗi định dạng ● ● Các hàm cùng họ printf() như fprintf(), sprintf() cũng mắc phải lỗi này Nguyên nhân là: ● ● Chuỗi nhập được sử dụng như chuỗi định dạng Hàm printf() cổ điển cho phép %n, %hn (các thư viện hiện đại đã bỏ %n và %hn) – ● Nhưng vẫn không ngăn được việc quét ngăn xếp Để tránh lỗi thì cần phải sử dụng hàm đúng cách, phải luôn luôn xác định rõ chuỗi định dạng khi muốn in tham số do người dùng nhập
- Xem thêm -

Tài liệu liên quan