當您調用像printf這樣的函數時,格式字符串和 參數被壓入堆棧。如果您省略參數,但 在格式字符串中用「%x」或「%s」或「%n」指定它們,則不能訪問(讀取或寫入)格式字符串。在我測試的一個系統上, 格式字符串是第四個參數。另一方面,它超過了第二百零二號的 。
不,你可能誤解了。
當您使用一個參數(格式字符串)調用printf時,指向格式字符串的指針將被壓入堆棧。這是一個char *
這個指針可以指向內存中的任何位置 - printf只是在它的位置被告知並將該內存位置作爲格式字符串讀取。
在通常的一個參數情況下,您將字符串文字傳遞給printf ("hello world!");
編譯器將文本hello world
放在內存中某處,並生成一個指向它的指針以傳遞給printf。然後它執行任何調用約定表示它應該爲函數調用執行的操作 - 例如,在x86上將指針壓入堆棧。然後Printf從堆棧讀取它的第一個參數,很高興!
在通常的n參數情況下,字符串文字和指針發生同樣的事情。對於函數調用,編譯器會傳遞每個值。再次使用x86(因爲推送比描述比較複雜的參數傳遞方式的ARM更容易描述)。這些值從右向左推送到堆棧。所以如果你有一個printf ("%d, %s, %d", x, name, y);
的調用,y被推入堆棧,然後命名,x,最後是格式字符串。
現在,在printf中我們讀取第一個參數(從堆棧中獲取它)。這是一個char *
,指向"%d, %s, %d"
。我們可以閱讀這個,然後 - 知道編譯器如何傳遞參數,我們可以讀出推入堆棧的三件事情 - 我們也很高興!
格式化字符串漏洞的工作原理是錯誤信念printf has和編譯器的信念。
我們可以通過調用傳遞給printf的錯誤數量參數引起的未定義行爲來顯示它。在調用printf ("%s");
編譯器不推送對應於printf期望使用的char *
來完成%s
指令的參數。但是 - 因爲printf不知道編譯器沒有這樣做,所以它無論如何都會在棧上查找參數。它從堆棧中拉出一個未定義的值並嘗試讀取它指向的字符串。
在你的情況下,你允許任意格式的字符串傳遞給printf。這些參數在預期的參數數量和傳遞的參數數量之間肯定不匹配,所以printf讀取堆棧 - 這是堆滿了垃圾。
如果你很幸運 - 你可以操縱這個垃圾指向你控制的東西 - 並且可以用它來讀取你沒有想到的信息。如果您可以欺騙%n參數指向您控制的某個位置,則可以使用打印的字符數寫入該內存位置。
所以 - 考慮到這個描述,我找不到解決你的問題的方法,這是有道理的。也許你可以更清楚,我可以更新我的答案?
你能提供一些示例代碼來說明你的問題嗎? – ObscureRobot
你說的是字符串*內容*的位置嗎?所有在堆棧中傳遞的是一個*指針*(即分配給它的地址)。對於可變參數函數,通常將參數從右向左推入,以便它位於堆棧的頂部(因此'printf'總能找到第一個參數)。 –