좀 열심히 쓴 글

Format String field width

ch4rli3kop 2020. 4. 9. 18:38
반응형

Format String

Format of the format string

포맷 스트링은 initial shift state(byte sequence에 대한 해석을 문자로 알리는 상태)에서 시작하고 끝나는 문자열을 말한다. 포맷 스트링은 0이상의 지시문으로 구성되는데, 다음과 같다.

  1. output stream으로 변경되지 않고 복사되는 (%이 아닌) 일반 문자 (ordinary characters)

  2. 0이상의 연이은 인자들을 가져오는 변환 명시 (conversion specifications) 각각의 conversion specification% 문자로 시작하여 conversion specifier로 끝난다. 그 사이에는 0개 이상의 flag와 optional한 minimum field width, precision, length modifier가 있을 수 있다.

인자들은 타입 지정 후 반드시 conversion specifier와 적절하게 대응되어야 한다. 기본적으로 인수는 주어진 순서대로 사용되며, *와 각 conversion specifier 다음 인수를 요청한다. % 대신에 %m$로, *대신에 *m$로 사용함으로써, 인수가 필요한 각 위치에서 어떤 인수를 취할지 명시적으로 지정할 수 있다. 그리고 여기서 10진수 정수 m은 1부터 시작하여 인덱싱된 인자 리스트의 위치를 나타낸다.

그러므로, 다음의 두 표현은 동일하다.

printf("%*d", width, num);
printf("%2$*1$d", width, num);

두 번째 스타일은 동일한 인수에 대한 반복 참조를 허용한다. C99 표준에는 단일 UNIX 사양에서 제공되는 $를 사용하는 스타일을 포함하지 않는다. $를 사용하는 스타일을 사용할 때는, 인수와 모든 widthprecision 인수를 사용하는 모든 conversion에 사용해야하는데, 인수를 사용하지 않는 %%형식과도 혼합하여 사용할 수 있다. $를 사용하여 지정된 인수의 수에는 차이가 없을 수 있다. 예를 들어, 인수 1과 3을 지정했다면 포맷 스트링 어딘가에 인수 2도 지정해야한다.


Field width

minimum field width를 지정하는 선택적인 10진수 문자열이다. 0으로 시작하지 않는다. 만약 변환된 값이 지정된 field width 값보다 적은 수의 문자를 갖는다면, 남은 공간은 space로 padding 된다. 10진수 문자열을 직접 하드코딩하는 것대신, *를 사용하거나 *m$를 사용하여, 다음 인자 혹은 m번째 인자에 주어진 field width를 지정한다. 각 타입은 int 형이여야 한다. 음수 값이면 - flag가 세팅되어 좌로 정렬된다. 존재하지 않거나 작은 field width로 인해서 값의 길이를 줄이지 않는다. 변환된 값이 field width보다 크다면, 해당 값을 포함할 수 있도록 field가 확장된다.

Example

#include <iostream>

using namespace std;

int main(){

   printf("%*\n", 10, 20); // %10
   // *는 다음 인자의 값을 해당 위치로 치환해줌
   
   printf("%*d\n", 10, 20); //         20
   // 10을 width로 갖는 20출력
   
   printf("%2$*1$d\n", 10, 20, 30, 40, 50); //         20
   // 20출력, width를 10으로 갖음
   
   return 0;
}

기출변형

#include <iostream>

using namespace std;

int main(){

   printf("%d%*d\n", 10, 20, 30); // 10                 30
   // 10이 출력되고, 20을 width로 갖는 30출력
   
   printf("%*2$1$d\n", 10, 20, 30, 40); // %201$d
   // 제대로된 형식이 아님. *2$에 20이 치환됨
   
   
   printf("%1$*3$d\n", 10, 20, 30, 40); //                             10
   // 10출력, 30을 width로 갖음
   
   printf("%1$d%3$d\n", 10, 20, 30, 40); // 1030
   // 10출력, 30출력
   
   printf("%2$%d\n", 10, 20, 30); // %d
   printf("%2$$%d\n", 10, 20, 30); // %$10
   printf("%1$2$%d\n", 10, 20, 30); // %2$10
   printf("%2$2$3$4$%d\n", 10, 20, 30); // %2$3$4$10
   
   return 0;
}


Exploit

이걸 이용하면 메모리 내에 있는 값을 %n을 이용하여 다른 영역에 바로 쓸 수 있음.

예를 들어 다음과 같은 경우, d에 있는 값 만큼 공백을 출력시킨뒤 c를 이용하여 a 값을 d의 값으로 업데이트 시킬 수 있다.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]){

   int a = 0x10;
   char b[16] = {};
   int* c = &a;
   int d = 0x20;

   fgets(b, 0x10, stdin);
   printf(b);
   if(a == 0x20)
       system("/bin/sh");

   return 0;
}


반응형