การใช้ตัว%n
ระบุรูปแบบใน C คืออะไร? ใครช่วยอธิบายด้วยตัวอย่าง
%n
ทำให้printf
Turing สมบูรณ์โดยไม่ได้ตั้งใจและคุณสามารถใช้ Brainfuck ได้เช่นกันโปรดดูที่github.com/HexHive/printbfและoilhell.org/blog/2019/02/07.html#appendix-a-minor-sublanguages
การใช้ตัว%n
ระบุรูปแบบใน C คืออะไร? ใครช่วยอธิบายด้วยตัวอย่าง
%n
ทำให้printf
Turing สมบูรณ์โดยไม่ได้ตั้งใจและคุณสามารถใช้ 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
ฉันไม่เคยเห็นการใช้%n
specifier ในโลกแห่งความเป็นจริงมากนัก แต่ฉันจำได้ว่ามันถูกใช้ในช่องโหว่ 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_chars
12
จากนั้นก็จะมีค่า
จนถึงตอนนี้คำตอบทั้งหมดก็เกี่ยวกับเรื่อง%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