파일의 내용을 읽기 위한 함수는 대표적으로 fread, fgets 함수 가 존재한다.
해당 함수는 라이브러리 내부에서 _IO_file_xsgetsn 함수를 호출한다.
_IO_new_file_xsgetn code
_IO_size_t
_IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n)
{
_IO_size_t want, have;
_IO_ssize_t count;
_char *s = data;
want = n;
...
/* If we now want less than a buffer, underflow and repeat
the copy. Otherwise, _IO_SYSREAD directly to
the user buffer. */
if (fp->_IO_buf_base
&& want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base))
{
if (__underflow (fp) == EOF)
break;
continue;
}
...
}
want 변수 즉 인자 n 값이 (fp->_IO_buf_end - fp->_IO_buf_base) 의 값, 즉 length 의 크기 보다 작은지를 검사하고 __underflow(fp) 를 호출한다.
_underflow(fp) —> _IO_new_file_underflow(fp)
_IO_new_file_underflow code
int _IO_new_file_underflow (FILE *fp)
{
ssize_t count;
if (fp->_flags & _IO_NO_READS)
{
fp->_flags |= _IO_ERR_SEEN;
__set_errno (EBADF);
return EOF;
}
...
count = _IO_SYSREAD (fp, fp->_IO_buf_base,
fp->_IO_buf_end - fp->_IO_buf_base);
}
_IO_new_file_underflow 함수는 내부에서 flags 값에 Read 권한이 부여 되어있는지 확인하고 _IO_SYSREAD(fp, fp→_IO_buf_base, fp→_IO_buf_end - fp→_IO_buf_base) 함수를 호출한다.
#define _IO_SYSREAD(FP, DATA, LEN) JUMP2 (__read, FP, DATA, LEN)
_IO_SYSREAD 함수는 위 매크로처럼 vtable의 _IO_file_read 함수이다.
_IO_file_read code
_IO_ssize_t
_IO_file_read (_IO_FILE *fp, void *buf, _IO_ssize_t size)
{
return (__builtin_expect (fp->_flags2 & _IO_FLAGS2_NOTCANCEL, 0)
? __read_nocancel (fp->_fileno, buf, size)
: __read (fp->_fileno, buf, size));
}
_IO_file_read 함수 내부에서 read 시스템 콜을 사용해 파일의 데이터를 읽는다 . 시스템 콜의 인자로 파일 구조체의 파일 디스크립터를 나타내는 _fileno, _IO_buf_base, _IO_buf_end - _IO_buf_base 를 전달하는 것을 알 수 있다.
read(f->_fileno, _IO_buf_base, _IO_buf_end - _IO_buf_base);
익스 시나리오
#include <stdio.h>
#include <unistd.h>
#include <string.h>
char account_buf[1024];
int overwrite_me;
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
int read_account() {
FILE *fp;
fp = fopen("/etc/passwd", "r");
fread(account_buf, sizeof(char), sizeof(account_buf), fp);
write(1, account_buf, sizeof(account_buf));
fclose(fp);
}
int main() {
FILE *fp;
char file_buf[1024];
init();
fp = fopen("/etc/issue", "r");
printf("Data: ");
read(0, fp, 300);
fread(file_buf, 1, sizeof(file_buf)-1, fp);
printf("%s", file_buf);
if( overwrite_me == 0xDEADBEEF)
read_account();
fclose(fp);
}
익스시 주의 사항.
_IO_new_file_underflow 함수 안에 보면 조건문 중에 _IO_buf_end - _IO_buf_base 값이 fread함수의 인자로 전달된 읽을 값(sizeof(file_buf)-1) 보다 크기가 커야하는 조건이 있음.
- 파일 구조체 조작
- 먼저 AAW를 하기 위해서 _IO_buf_end 와 _IO_buf_base를 조작해야 한다.
번외... 전역변수를 자세히 보아야 하는 이유. coderead(f->_fileno, _IO_buf_base, _IO_buf_end - _IO_buf_base);
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[520]; // [rsp+10h] [rbp-220h] BYREF
void *src; // [rsp+218h] [rbp-18h]
void *dest; // [rsp+220h] [rbp-10h]
int v7; // [rsp+22Ch] [rbp-4h]
v7 = 0;
dest = 0LL;
src = 0LL;
memset(s, 0, 0x1FFuLL);
initialize(s);
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
printf("# ");
read_command(s);
if ( strcmp(s, "read") )
break;
read_str();
}
if ( strcmp(s, "help") )
break;
help();
}
if ( strncmp(s, "printf", 6uLL) )
break;
if ( strtok(s, " ") )
{
src = strtok(0LL, " ");
dest = stdin;
if ( src )
memcpy(dest, src, 0x40uLL);
}
}
if ( !strcmp(s, "exit") )
break;
printf("%s: command not found\n", s);
}
return 0;
}
.data:0000000000602000 ; ===========================================================================
.data:0000000000602000
.data:0000000000602000 ; Segment type: Pure data
.data:0000000000602000 ; Segment permissions: Read/Write
.data:0000000000602000 _data segment qword public 'DATA' use64
.data:0000000000602000 assume cs:_data
.data:0000000000602000 ;org 602000h
.data:0000000000602000 public __data_start ; weak
.data:0000000000602000 __data_start db 0 ; Alternative name is '__data_start'
.data:0000000000602000 ; data_start
.data:0000000000602001 db 0
.data:0000000000602002 db 0
.data:0000000000602003 db 0
.data:0000000000602004 db 0
.data:0000000000602005 db 0
.data:0000000000602006 db 0
.data:0000000000602007 db 0
.data:0000000000602008 public __dso_handle
.data:0000000000602008 __dso_handle db 0
.data:0000000000602009 db 0
.data:000000000060200A db 0
.data:000000000060200B db 0
.data:000000000060200C db 0
.data:000000000060200D db 0
.data:000000000060200E db 0
.data:000000000060200F db 0
.data:0000000000602010 public size
.data:0000000000602010 size dd 200h ; DATA XREF: read_command+C↑r
.data:0000000000602010 _data ends
.data:0000000000602010
LOAD:0000000000602014 ; ===========================================================================
LOAD:0000000000602014
LOAD:0000000000602014 ; Segment type: Pure data
LOAD:0000000000602014 ; Segment permissions: Read/Write
LOAD:0000000000602014 LOAD segment byte public 'DATA' use64
LOAD:0000000000602014 assume cs:LOAD
LOAD:0000000000602014 ;org 602014h
LOAD:0000000000602014 public __bss_start
LOAD:0000000000602014 __bss_start db ? ; ; Alternative name is '__bss_start'
LOAD:0000000000602014 ; _edata
LOAD:0000000000602015 db ? ;
LOAD:0000000000602016 db ? ;
LOAD:0000000000602017 db ? ;
LOAD:0000000000602018 public __TMC_END__
LOAD:0000000000602018 __TMC_END__ db ? ; ; DATA XREF: deregister_tm_clones+6↑o
LOAD:0000000000602018 ; deregister_tm_clones+20↑o ...
LOAD:0000000000602019 db ? ;
LOAD:000000000060201A db ? ;
LOAD:000000000060201B db ? ;
LOAD:000000000060201C db ? ;
LOAD:000000000060201D db ? ;
LOAD:000000000060201E db ? ;
LOAD:000000000060201F unk_60201F db ? ; ; DATA XREF: deregister_tm_clones↑o
LOAD:000000000060201F LOAD ends
LOAD:000000000060201F
.bss:0000000000602020 ; ===========================================================================
.bss:0000000000602020
.bss:0000000000602020 ; Segment type: Uninitialized
.bss:0000000000602020 ; Segment permissions: Read/Write
.bss:0000000000602020 _bss segment align_32 public 'BSS' use64
.bss:0000000000602020 assume cs:_bss
.bss:0000000000602020 ;org 602020h
.bss:0000000000602020 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.bss:0000000000602020 public stdout@@GLIBC_2_2_5
.bss:0000000000602020 ; FILE *stdout
.bss:0000000000602020 stdout@@GLIBC_2_2_5 dq ? ; DATA XREF: LOAD:00000000004004F0↑o
.bss:0000000000602020 ; initialize+22↑r
.bss:0000000000602020 ; Alternative name is 'stdout'
.bss:0000000000602020 ; Copy of shared data
.bss:0000000000602028 align 10h
.bss:0000000000602030 public stdin@@GLIBC_2_2_5
.bss:0000000000602030 ; FILE *stdin
.bss:0000000000602030 stdin@@GLIBC_2_2_5 dq ? ; DATA XREF: LOAD:0000000000400478↑o
.bss:0000000000602030 ; initialize+4↑r ...
.bss:0000000000602030 ; Alternative name is 'stdin'
.bss:0000000000602030 ; Copy of shared data
.bss:0000000000602038 completed_7594 db ? ; DATA XREF: __do_global_dtors_aux↑r
.bss:0000000000602038 ; __do_global_dtors_aux+13↑w
.bss:0000000000602039 align 20h
.bss:0000000000602040 public buf
.bss:0000000000602040 ; char buf[80]
.bss:0000000000602040 buf db 50h dup(?) ; DATA XREF: read_str+13↑o
.bss:0000000000602040 _bss ends
.bss:0000000000602040
.prgend:0000000000602090 ; ===========================================================================
전역변수에 buf가 허걱 ㄷㄷ