การใช้ตัว%nระบุรูปแบบใน C คืออะไร? ใครช่วยอธิบายด้วยตัวอย่าง
%nทำให้printfTuring สมบูรณ์โดยไม่ได้ตั้งใจและคุณสามารถใช้ Brainfuck ได้เช่นกันโปรดดูที่github.com/HexHive/printbfและoilhell.org/blog/2019/02/07.html#appendix-a-minor-sublanguages
การใช้ตัว%nระบุรูปแบบใน C คืออะไร? ใครช่วยอธิบายด้วยตัวอย่าง
%nทำให้printfTuring สมบูรณ์โดยไม่ได้ตั้งใจและคุณสามารถใช้ Brainfuck ได้เช่นกันโปรดดูที่github.com/HexHive/printbfและoilhell.org/blog/2019/02/07.html#appendix-a-minor-sublanguages
คำตอบ:
ไม่มีอะไรพิมพ์ อาร์กิวเมนต์ต้องเป็นตัวชี้ไปยัง int ที่มีการลงนามซึ่งจำนวนอักขระที่เขียนจนถึงตอนนี้จะถูกเก็บไว้
#include <stdio.h>
int main()
{
int val;
printf("blah %n blah\n", &val);
printf("val = %d\n", val);
return 0;
}
รหัสก่อนหน้านี้พิมพ์:
blah blah
val = 5
intเซ็นตลอด
n format specifies disabledด้วยเหตุผลบางอย่างตัวอย่างยกข้อผิดพลาดมีหมายเหตุ มีเหตุผลอะไร?
ส่วนใหญ่คำตอบเหล่านี้อธิบายสิ่งที่%n ไม่ (ซึ่งเป็นพิมพ์อะไรและจะเขียนจำนวนตัวอักษรที่พิมพ์ป่านนี้ไปยังintตัวแปร) แต่จนถึงขณะนี้ไม่มีใครได้รับจริงๆตัวอย่างของสิ่งที่ใช้ก็มี นี่คือหนึ่ง:
int n;
printf("%s: %nFoo\n", "hello", &n);
printf("%*sBar\n", n, "");
จะพิมพ์:
hello: Foo
Bar
โดยจัดแนว Foo และ Bar (เป็นเรื่องเล็กน้อยที่จะทำเช่นนั้นโดยไม่ใช้%nสำหรับตัวอย่างนี้โดยเฉพาะและโดยทั่วไปแล้วการprintfโทรครั้งแรกนั้นอาจแตกได้:
int n = printf("%s: ", "hello");
printf("Foo\n");
printf("%*sBar\n", n, "");
ความสะดวกสบายที่เพิ่มขึ้นเล็กน้อยนั้นคุ้มค่ากับการใช้สิ่งที่ลึกลับเช่น%n(และอาจเกิดข้อผิดพลาด) หรือไม่นั้นเปิดให้มีการถกเถียงกัน)
&nเป็นตัวชี้ (&คือแอดเดรสของตัวดำเนินการ); จำเป็นต้องมีตัวชี้เนื่องจาก C เป็น pass-by-value และหากไม่มีตัวชี้printfก็ไม่สามารถแก้ไขค่าของn. การ%*sใช้งานในprintfสตริงรูปแบบจะพิมพ์ตัว%sระบุ (ในกรณีนี้คือสตริงว่าง"") โดยใช้ความกว้างของฟิลด์nอักขระ คำอธิบายprintfหลักการพื้นฐานนั้นอยู่นอกขอบเขตของคำถามนี้ (และคำตอบ) ฉันขอแนะนำให้อ่านprintfเอกสารหรือถามคำถามแยกต่างหากของคุณใน SO
ฉันไม่เคยเห็นการใช้%nspecifier ในโลกแห่งความเป็นจริงมากนัก แต่ฉันจำได้ว่ามันถูกใช้ในช่องโหว่ printf oldschool ด้วยการโจมตีสตริงรูปแบบในขณะที่ย้อนกลับไป
สิ่งที่เป็นเช่นนี้
void authorizeUser( char * username, char * password){
...code here setting authorized to false...
printf(username);
if ( authorized ) {
giveControl(username);
}
}
ซึ่งผู้ใช้ที่ประสงค์ร้ายสามารถใช้ประโยชน์จากพารามิเตอร์ชื่อผู้ใช้ที่ส่งผ่านไปยัง printf เป็นสตริงรูปแบบและใช้การรวมกันของ %d ,%cหรือ W / E ที่จะไปผ่านสาย stack แล้วปรับเปลี่ยนตัวแปรที่ได้รับอนุญาตให้เป็นค่าที่แท้จริง
ใช่มันเป็นการใช้งานที่ลึกลับ แต่มีประโยชน์เสมอที่จะรู้เมื่อเขียนภูตเพื่อหลีกเลี่ยงช่องโหว่ด้านความปลอดภัย? : D
%nจะหลีกเลี่ยงการใช้สตริงอินพุตที่ไม่ได้เลือกเป็นprintfสตริงรูปแบบ
จากตรงนี้เราจะเห็นว่ามันเก็บจำนวนอักขระที่พิมพ์จนถึงตอนนี้
nอาร์กิวเมนต์จะเป็นตัวชี้ไปยังจำนวนเต็มซึ่งเขียนจำนวนไบต์ที่เขียนไปยังเอาต์พุตโดยการเรียกนี้ไปยังfprintf()ฟังก์ชันใดฟังก์ชันหนึ่ง ไม่มีการเปลี่ยนข้อโต้แย้ง
ตัวอย่างการใช้งานจะเป็น:
int n_chars = 0;
printf("Hello, World%n", &n_chars);
n_chars12จากนั้นก็จะมีค่า
จนถึงตอนนี้คำตอบทั้งหมดก็เกี่ยวกับเรื่อง%nนี้ แต่ไม่ใช่ว่าทำไมใคร ๆ ก็อยากได้ตั้งแต่แรก ฉันคิดว่ามันค่อนข้างมีประโยชน์กับsprintf/ snprintfเมื่อคุณอาจต้องแยกหรือแก้ไขสตริงผลลัพธ์ในภายหลังเนื่องจากค่าที่เก็บไว้เป็นดัชนีอาร์เรย์ในสตริงผลลัพธ์ อย่างไรก็ตามแอปพลิเคชันนี้มีประโยชน์มากกว่าsscanfโดยเฉพาะอย่างยิ่งเมื่อมีฟังก์ชันในไฟล์scanfตระกูลไม่ส่งคืนจำนวนอักขระที่ประมวลผล แต่เป็นจำนวนฟิลด์
การใช้งานที่แฮ็กจริงๆอีกอย่างหนึ่งคือการได้รับ pseudo-log10 ฟรีในเวลาเดียวกันในขณะที่พิมพ์ตัวเลขเป็นส่วนหนึ่งของการดำเนินการอื่น
%nแม้ว่าฉันขอแตกต่างกันเกี่ยวกับ "คำตอบทั้งหมด ... " = P
%nไม่มีความเสี่ยงอย่างแท้จริงจากความเสี่ยงต่อผู้โจมตี ความน่าอับอายที่ใส่ผิดตำแหน่ง%nจริงๆเป็นของการฝึกฝนโง่ ๆ ในการส่งสตริงข้อความแทนที่จะเป็นสตริงรูปแบบเป็นอาร์กิวเมนต์รูปแบบ แน่นอนว่าสถานการณ์นี้ไม่เคยเกิดขึ้นเมื่อ%nเป็นส่วนหนึ่งของสตริงรูปแบบเจตนาที่ใช้จริง
*p = f();สำหรับการเขียน เหตุใด%nซึ่งเป็นเพียงอีกวิธีหนึ่งในการเขียนผลลัพธ์ไปยังวัตถุที่ชี้โดยตัวชี้จึงถูกมองว่า "อันตราย" แทนที่จะพิจารณาว่าตัวชี้นั้นเป็นอันตราย
อาร์กิวเมนต์ที่เกี่ยวข้องกับ%nจะถือว่าเป็น an int*และเต็มไปด้วยจำนวนอักขระทั้งหมดที่พิมพ์ ณ จุดนั้นในไฟล์printf.
เมื่อวันก่อนฉันพบว่าตัวเองอยู่ในสถานการณ์ที่%nจะช่วยแก้ปัญหาของฉันได้เป็นอย่างดี ไม่เหมือนกับคำตอบก่อนหน้านี้ในกรณีนี้ฉันไม่สามารถคิดทางเลือกที่ดีได้
ฉันมีตัวควบคุม GUI ที่แสดงข้อความที่ระบุ การควบคุมนี้สามารถแสดงบางส่วนของข้อความนั้นเป็นตัวหนา (หรือตัวเอียงหรือขีดเส้นใต้ ฯลฯ ) และฉันสามารถระบุส่วนใดได้โดยการระบุดัชนีอักขระเริ่มต้นและสิ้นสุด
ในกรณีของฉันฉันกำลังสร้างข้อความให้กับตัวควบคุมsnprintfและฉันต้องการให้การแทนที่ตัวใดตัวหนึ่งเป็นตัวหนา การค้นหาดัชนีเริ่มต้นและสิ้นสุดของการแทนที่นี้ไม่สำคัญเนื่องจาก:
สตริงประกอบด้วยการแทนที่หลายครั้งและการแทนที่อย่างใดอย่างหนึ่งเป็นข้อความที่ผู้ใช้ระบุโดยพลการ ซึ่งหมายความว่าการค้นหาข้อความสำหรับสิ่งทดแทนที่ฉันสนใจอาจมีความคลุมเครือ
สตริงรูปแบบอาจเป็นภาษาท้องถิ่นและอาจใช้$ส่วนขยาย POSIX สำหรับตัวระบุรูปแบบตำแหน่ง ดังนั้นการค้นหาสตริงรูปแบบดั้งเดิมสำหรับตัวระบุรูปแบบเองจึงไม่สำคัญ
snprintfด้านการแปลก็หมายความว่าฉันไม่สามารถเลิกสตริงรูปแบบออกเป็นหลายสายไป
ดังนั้นวิธีที่ตรงไปตรงมาที่สุดในการค้นหาดัชนีรอบ ๆ การแทนที่เฉพาะคือทำ:
char buf[256];
int start;
int end;
snprintf(buf, sizeof buf,
"blah blah %s %f yada yada %n%s%n yakety yak",
someUserSpecifiedString,
someFloat,
&start, boldString, &end);
control->set_text(buf);
control->set_bold(start, end);
snprintfในขณะที่ตรวจสอบค่าส่งคืนจะทำงานได้ดีเนื่องจากsnprintfส่งคืนจำนวนอักขระที่เขียน บางทีสิ่งที่ชอบ: int begin = snprintf(..., "blah blah %s %f yada yada", ...);และแล้วหาง:int end = snprintf(..., "%s", ...); snprintf(..., "blah blah");
snprintfเรียกหลายสายคือการแทนที่อาจถูกจัดเรียงใหม่ในภาษาอื่นดังนั้นจึงไม่สามารถแยกออกได้เช่นนั้น
%n; ฉันอ้างว่าการใช้%nนั้นตรงไปตรงมามากกว่าทางเลือกอื่น
มันไม่พิมพ์อะไรเลย ใช้เพื่อหาจำนวนอักขระที่พิมพ์ก่อนที่จะ%nปรากฏในสตริงรูปแบบและส่งออกไปยัง int ที่ให้มา:
#include <stdio.h>
int main(int argc, char* argv[])
{
int resultOfNSpecifier = 0;
_set_printf_count_output(1); /* Required in visual studio */
printf("Some format string%n\n", &resultOfNSpecifier);
printf("Count of chars before the %%n: %d\n", resultOfNSpecifier);
return 0;
}
จะเก็บค่าของจำนวนอักขระที่พิมพ์ในprintf()ฟังก์ชันนั้น ๆ
ตัวอย่าง:
int a;
printf("Hello World %n \n", &a);
printf("Characters printed so far = %d",a);
ผลลัพธ์ของโปรแกรมนี้จะเป็น
Hello World
Characters printed so far = 12
% n คือ C99 ไม่ทำงานกับ VC ++
%nมีอยู่ใน C89 ไม่ทำงานกับ MSVC เนื่องจาก Microsoft ปิดใช้งานโดยค่าเริ่มต้นเนื่องจากปัญหาด้านความปลอดภัย คุณต้องโทร_set_printf_count_outputก่อนเพื่อเปิดใช้งาน (ดูคำตอบของ Merlyn Morgan-Graham)
printfการแปลง) ของภาคผนวก B ของ K&R ฉบับที่ 2 (หน้า 244 ของสำเนาของฉัน) หรือดูหัวข้อ 7.9.6.1 (หน้า 134) ของข้อกำหนด ISO C90