mprotect() 함수
int mprotect(const void * addr, size_t len, int prot);
메모리 영역 보호 설정하기
호출 프로세스의 메모리 페이지들 가운데 addr, addr+len -1 주소 범위를 포함하고 잇는 것들에 대한 보호 방식을 변경 한다.
PROT_NONE : 접근할 수 없다.
PROT_READ : 읽기
PROT_WRITE : 쓰기
PROT_EXEC : 실행
이걸로 다음과 같은 작업을 할수 있다.
함수를 함수 포인터에 카피한다음
그걸 수행 하게 할수 있다.
즉 heap영역에 코드를 복사하고 수행 할수도 있다.
func(); < --- 어떤 수행을 하는 코드가 있다고 하고
memcpy( p, func , 대강 크기);
p어드레스를 수행 가능하게 mprotect()를 해주면
p를 수행 시킬수도 있다.
ex) binary hacks라는 책에서 발췌
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
double func(void)
{
return 3.14;
}
void allow_execution(const void *addr)
{
long pagesize = (int)sysconf(_SC_PAGESIZE);
char *p = (char *)((long)addr & ~(pagesize -1L));
mprotect(p, pagesize * 10L, PROT_READ | PROT_WRITE | PROT_EXEC);
}
int main(int argc, char **argv)
{
void *p = malloc(100);
memcpy(p, func, 1000);
allow_execution(p);
printf("PI equals to %g\n", ((double (*) (void))p) ());
}
$./mprotect2
PI equals to 3.14
2011년 3월 17일 목요일
2011년 2월 25일 금요일
stack overflow 상태에서 sigaction처리 하기.
sigaction()
signal handler를 작성해서 특정 시그널에 따라 어떤 조치를 취하도록 할 수 있다.
시그널이 발생했을 때 에러 원인을 파악하기 위해
backtrace()
backtrace_symbol()
함수를 이용해서 호출 스택까지도 출력할 수 있다.
문제는 stack overflow가 발생해서 SIGSEGV 가 된경우 이를 처리할 함수 조차 호출 할 수 없다.
이를 해결 하기 위해 sigaltstack()함수로 미리 스택영역을 잡아두는 방법이 있다.
$cat stack_overflow.c
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define STACK_SIZE ( 4 * 1024 )
static int is_altstack_defined =0 ;
static char tmp_stack[STACK_SIZE];
void sig_segv(int signo);
int foo()
{
return foo();
}
void sig_segv(int signo)
{
printf("stack overflow\n");
exit(-1);
}
static void register_sigaltstack()
{
stack_t newSS, oldSS;
if(is_altstack_defined)
{
return;
}
newSS.ss_sp = tmp_stack;
newSS.ss_size = STACK_SIZE;
newSS.ss_flags = 0;
if(sigaltstack(&newSS, &oldSS) < 0)
{
printf ("error altstack");
}
is_altstack_defined = 1;
}
int main(int argc, char * argv[])
{
int i = 0;
struct sigaction sigsegv;
register_sigaltstack();
sigsegv.sa_handler = sig_segv;
sigemptyset(&sigsegv.sa_mask);
sigsegv.sa_flags = SA_ONSTACK;
if( sigaction(SIGSEGV, &sigsegv, 0 ) == -1)
{
printf("signal SIGSEGV error");
return -1;
}
foo();
}
signal handler를 작성해서 특정 시그널에 따라 어떤 조치를 취하도록 할 수 있다.
시그널이 발생했을 때 에러 원인을 파악하기 위해
backtrace()
backtrace_symbol()
함수를 이용해서 호출 스택까지도 출력할 수 있다.
문제는 stack overflow가 발생해서 SIGSEGV 가 된경우 이를 처리할 함수 조차 호출 할 수 없다.
이를 해결 하기 위해 sigaltstack()함수로 미리 스택영역을 잡아두는 방법이 있다.
$cat stack_overflow.c
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define STACK_SIZE ( 4 * 1024 )
static int is_altstack_defined =0 ;
static char tmp_stack[STACK_SIZE];
void sig_segv(int signo);
int foo()
{
return foo();
}
void sig_segv(int signo)
{
printf("stack overflow\n");
exit(-1);
}
static void register_sigaltstack()
{
stack_t newSS, oldSS;
if(is_altstack_defined)
{
return;
}
newSS.ss_sp = tmp_stack;
newSS.ss_size = STACK_SIZE;
newSS.ss_flags = 0;
if(sigaltstack(&newSS, &oldSS) < 0)
{
printf ("error altstack");
}
is_altstack_defined = 1;
}
int main(int argc, char * argv[])
{
int i = 0;
struct sigaction sigsegv;
register_sigaltstack();
sigsegv.sa_handler = sig_segv;
sigemptyset(&sigsegv.sa_mask);
sigsegv.sa_flags = SA_ONSTACK;
if( sigaction(SIGSEGV, &sigsegv, 0 ) == -1)
{
printf("signal SIGSEGV error");
return -1;
}
foo();
}
위코드를 스레드로 변경 하면 동작 하지 않는다.
스레드에서 foo() 무한 재귀 함수를 호출 해서 스택 오버플로우가 발생했을때는 동작 하지 않는다.
여러 삽질 끝에 답을 알아 냈는데.
스레드 마다. sigaltstack() 을 해줘야 동작 한다.
여기에 관해서 찾은 글귀..
Operating in a multithreaded environment
If a process calls
sigaction and then spawns pthreads within it, then those pthreads will inherit the signal handlers that were already installed. Apparently, this is not the case forsigaltstack: If a signal handler is installed with sigaction using a sigaltstack, and a thread spawned from that process is killed with the right signal, then the installed stack will not be found! The signal handler must instead be installed on each pthread individually. I’m not sure whether this is a bug in Linux or just a quirk of POSIX; in any case, I couldn’t find it documented anywhere.2011년 2월 23일 수요일
stack overflow , 컴파일 옵티마이저 레벨
stack overflow
옵티마이저 레벨에 따라 SIGSEGV가 나지 않고 무한 대기 할때가 있다.
$ gcc -O3 stack_overflow.c -o stack_overflow
$ ./stack_overflow
$ gcc -O2 stack_overflow.c -o stack_overflow
$ ./stack_overflow
$ gcc -O1 stack_overflow.c -o stack_overflow
$ ./stack_overflow
세그멘테이션 오류
$ cat stack_overflow.c
int foo()
{
return foo();
}
int main(int argc, char * argv[])
{
foo();
}
x86_64 linux에서 gcc 4.1.2
-O3, -O2 는 위코드가 무한대기 ctrl + c로 중지 시킨것이고
-O1 에서는 SIGSEGV가 발생한다.
옵티마이저 레벨에 따라 SIGSEGV가 나지 않고 무한 대기 할때가 있다.
$ gcc -O3 stack_overflow.c -o stack_overflow
$ ./stack_overflow
$ gcc -O2 stack_overflow.c -o stack_overflow
$ ./stack_overflow
$ gcc -O1 stack_overflow.c -o stack_overflow
$ ./stack_overflow
세그멘테이션 오류
$ cat stack_overflow.c
int foo()
{
return foo();
}
int main(int argc, char * argv[])
{
foo();
}
x86_64 linux에서 gcc 4.1.2
-O3, -O2 는 위코드가 무한대기 ctrl + c로 중지 시킨것이고
-O1 에서는 SIGSEGV가 발생한다.
컴파일러 최적화를 하면 오히려 스택오버플로우가 발생하지 않는것일까?
2010년 11월 4일 목요일
computed goto
재미있는 주제가 있어서 글을 써본다.
C언어에서 잘 사용하지 않는 keyword goto
그래서 더욱더 모르고 잇는듯한.. goto
computed goto라는 용어가 맞는지 틀리는지는 모르지만 일단 goto로 점프할 레이블에 번지수를 변수에 넣고
변수를 이용해서 점프를 한다.
Labels as Values
You can get the address of a label defined in the current function (or a containing function) with the unary operator &&. The value has type void *. This value is a constant and can be used wherever a constant of that type is valid. For example:
void *ptr;
/* ... */
ptr = &&foo;
To use these values, you need to be able to jump to one. This is done with the computed goto statement1, goto *exp;. For example,
goto *ptr;
Any expression of type void * is allowed.
One way of using these constants is in initializing a static array that will serve as a jump table:
static void *array[] = { &&foo, &&bar, &&hack };
Then you can select a label with indexing, like this:
goto *array[i];
Note that this does not check whether the subscript is in bounds--array indexing in C never does that.
Such an array of label values serves a purpose much like that of the switch statement. The switch statement is cleaner, so use that rather than an array unless the problem does not fit a switch statement very well.
Another use of label values is in an interpreter for threaded code. The labels within the interpreter function can be stored in the threaded code for super-fast dispatching.
You may not use this mechanism to jump to code in a different function. If you do that, totally unpredictable things will happen. The best way to avoid this is to store the label address only in automatic variables and never pass it as an argument.
An alternate way to write the above example is
static const int array[] = { &&foo - &&foo, &&bar - &&foo,
&&hack - &&foo };
goto *(&&foo + array[i]);
This is more friendly to code living in shared libraries, as it reduces the number of dynamic relocations that are needed, and by consequence, allows the data to be read-only.
Footnotes
The analogous feature in Fortran is called an assigned goto, but that name seems inappropriate in C, where one can do more than simply store label addresses in label variables.
원문:http://gcc.gnu.org/onlinedocs/gcc-3.4.1/gcc/Labels-as-Values.html
C언어에서 잘 사용하지 않는 keyword goto
그래서 더욱더 모르고 잇는듯한.. goto
computed goto라는 용어가 맞는지 틀리는지는 모르지만 일단 goto로 점프할 레이블에 번지수를 변수에 넣고
변수를 이용해서 점프를 한다.
Labels as Values
You can get the address of a label defined in the current function (or a containing function) with the unary operator &&. The value has type void *. This value is a constant and can be used wherever a constant of that type is valid. For example:
void *ptr;
/* ... */
ptr = &&foo;
To use these values, you need to be able to jump to one. This is done with the computed goto statement1, goto *exp;. For example,
goto *ptr;
Any expression of type void * is allowed.
One way of using these constants is in initializing a static array that will serve as a jump table:
static void *array[] = { &&foo, &&bar, &&hack };
Then you can select a label with indexing, like this:
goto *array[i];
Note that this does not check whether the subscript is in bounds--array indexing in C never does that.
Such an array of label values serves a purpose much like that of the switch statement. The switch statement is cleaner, so use that rather than an array unless the problem does not fit a switch statement very well.
Another use of label values is in an interpreter for threaded code. The labels within the interpreter function can be stored in the threaded code for super-fast dispatching.
You may not use this mechanism to jump to code in a different function. If you do that, totally unpredictable things will happen. The best way to avoid this is to store the label address only in automatic variables and never pass it as an argument.
An alternate way to write the above example is
static const int array[] = { &&foo - &&foo, &&bar - &&foo,
&&hack - &&foo };
goto *(&&foo + array[i]);
This is more friendly to code living in shared libraries, as it reduces the number of dynamic relocations that are needed, and by consequence, allows the data to be read-only.
Footnotes
The analogous feature in Fortran is called an assigned goto, but that name seems inappropriate in C, where one can do more than simply store label addresses in label variables.
원문:http://gcc.gnu.org/onlinedocs/gcc-3.4.1/gcc/Labels-as-Values.html
2010년 10월 25일 월요일
c array initialize { 0 }; or { 0, };
얼마전에 { 0 ,}; 이렇게 배열을 초기화 하는 것을 당연 하게 생각 했는데..
인터넷에 검색 해보니 { 0 } 이렇게 알고 있는 사람도 많은듯 하여
리눅스 에서 gcc로 테스트 해보았다.
$ cat array_init.c
#include
int main (void)
{
int a[10]={ 0 };
int b[10]={ 0,};
int c[10]={ 1 };
int d[10]={ 1,};
int e[10];
printf ("a[] %d %d \n", a[0], a[1]);
printf ("b[] %d %d \n", b[0], b[1]);
printf ("c[] %d %d \n", c[0], c[1]);
printf ("d[] %d %d \n", d[0], d[1]);
printf ("e[] %d %d \n", e[0], e[1]);
}
$ gcc -o array_init array_init.c
$ ./array_init
a[] 0 0
b[] 0 0
c[] 1 0
d[] 1 0
e[] 0 0
$ gcc -O2 -o array_init array_init.c
$ ./array_init
a[] 0 0
b[] 0 0
c[] 1 0
d[] 1 0
e[] 1478605760 1478605760
{ 0 } , { 0, } 모두 초기화 된다.
옵티마이즈 레벨을 주지 않으면 배열은 그냥 초기화 된다.
(디 버깅 시에 지역 변수들이 쓰레기 값을 가지지 앟고 침착 하게 초기화 되어 잇는 상황을 자주 보았을 듯..)
{ 1 }, { 1, } 가 1로 모두 초기화 하라는 뜻은 아니다.
이런 경우 memset() 을 이용 해야 겟다.
인터넷에 검색 해보니 { 0 } 이렇게 알고 있는 사람도 많은듯 하여
리눅스 에서 gcc로 테스트 해보았다.
$ cat array_init.c
#include
int main (void)
{
int a[10]={ 0 };
int b[10]={ 0,};
int c[10]={ 1 };
int d[10]={ 1,};
int e[10];
printf ("a[] %d %d \n", a[0], a[1]);
printf ("b[] %d %d \n", b[0], b[1]);
printf ("c[] %d %d \n", c[0], c[1]);
printf ("d[] %d %d \n", d[0], d[1]);
printf ("e[] %d %d \n", e[0], e[1]);
}
$ gcc -o array_init array_init.c
$ ./array_init
a[] 0 0
b[] 0 0
c[] 1 0
d[] 1 0
e[] 0 0
$ gcc -O2 -o array_init array_init.c
$ ./array_init
a[] 0 0
b[] 0 0
c[] 1 0
d[] 1 0
e[] 1478605760 1478605760
{ 0 } , { 0, } 모두 초기화 된다.
옵티마이즈 레벨을 주지 않으면 배열은 그냥 초기화 된다.
(디 버깅 시에 지역 변수들이 쓰레기 값을 가지지 앟고 침착 하게 초기화 되어 잇는 상황을 자주 보았을 듯..)
{ 1 }, { 1, } 가 1로 모두 초기화 하라는 뜻은 아니다.
이런 경우 memset() 을 이용 해야 겟다.
2010년 10월 22일 금요일
NULL to strlen() function ??
cat test.c
#include
#include
int main(void)
{
char * p;
p = NULL;
printf("%d\n",strlen(p));
}
$ gcc -o test test.c
$ ./test
세그멘테이션 오류 (core dumped)
NULL 은 문자열이 아니다.
그러니 쓰면 안되겟지만...
0을 리턴 할거 같은 기대감은 버리자..
피드 구독하기:
글 (Atom)