'System'에 해당되는 글 7건

  1. 2017.05.16 How 2 heap - fastbin_dup.c
  2. 2017.05.15 How 2 heap - first_fit.c
  3. 2016.08.24 LLVM -3 (2)
  4. 2016.08.17 LLVM - 2
  5. 2016.08.12 LLVM - 1
  6. 2015.04.01 프로그램에서 일어날수 있는 레이스컨디션의 예제 (8)
  7. 2014.12.26 사기를 위한 프로그램 분석

원문 : https://github.com/shellphish/how2heap/blob/master/fastbin_dup.c



#include 
#include 

int main()
{
	printf("이 파일은 fastbins을 통한 double-free 공격을 간단하게 보여줄 겁니다.\n");

	printf("3개의 버퍼를 할당하도록 하죠.\n");
	int *a = malloc(8);
	int *b = malloc(8);
	int *c = malloc(8);

	printf("1st malloc(8): %p\n", a);
	printf("2nd malloc(8): %p\n", b);
	printf("3rd malloc(8): %p\n", c);

	printf("제일 처음것을 할당해제를 하고...\n");
	free(a);

	printf("만약 %p 를 다시 해제한다면 크래쉬가 날텐데, %p가 free list의 제일 위에 있기 때문이죠.\n", a, a);
	// free(a);

	printf("그러므로 대신 %p를 할당해제 합시다..\n", b);
	free(b);

	printf("그럼 이제 %p를 할당해제 할수 있습니다. free list의 처음 노드가 아니기 때문이죠.\n", a);
	free(a);

	printf("free list는 현재 [ %p, %p, %p ] 입니다. 우리가 3회 할당하면, %p를 2번 얻게 될 겁니다.\n", a, b, a, a);
	printf("1st malloc(8): %p\n", malloc(8));
	printf("2nd malloc(8): %p\n", malloc(8));
	printf("3rd malloc(8): %p\n", malloc(8));
}


'System' 카테고리의 다른 글

How 2 heap - fastbin_dup.c  (0) 2017.05.16
How 2 heap - first_fit.c  (0) 2017.05.15
LLVM -3  (2) 2016.08.24
LLVM - 2  (0) 2016.08.17
LLVM - 1  (0) 2016.08.12
프로그램에서 일어날수 있는 레이스컨디션의 예제  (8) 2015.04.01
Posted by Maid:: IzayoiSakuya

How 2 heap - first_fit.c

System 2017.05.15 16:47

원문 : https://github.com/shellphish/how2heap/blob/master/first_fit.c 

#include 
#include 
#include 

int main()
{
	printf("이 파일은 POC는 아닙니다. 하지만 glibc's allocator의 본모습을 보여주게 될것입니다.\n");
	printf("glibc 비어있는 청크에서 최초적합 알고리즘을 사용하여 선택후, 가져옵니다.\n");
	printf("만약 청크가 비어있고 충분히 크다면, malloc은 그 청크를 선택하고 가져오겠죠.\n");
	printf("이는 Use-after-free 상황이며 공격 당할 수 있습니다\n");

	printf("2개의 버퍼를 할당합시다. fastbin이 아니도록 충분히 커야만 합니다.\n");
	char* a = malloc(512);
	char* b = malloc(256);
	char* c;

	printf("1st malloc(512): %p\n", a);
	printf("2nd malloc(256): %p\n", b);
	printf("우리는 여기에 더 할당 할 수 있습니다.\n");
	printf("문자열을 할당하여 읽을수 있게 합니다. \"this is A!\"를 버퍼에 넣도록 합시다.\n");
	strcpy(a, "this is A!");
	printf("첫 할당된 메모리 %p, 내용은 %s\n", a, a);

	printf("첫번째 것을 할당 해제 합시다.\n");
	free(a);

	printf("다시 다른것을 할당해제 할 필요는 없습니다. 512 보다 작게 할당하는 한, %p에서 그칠테니까요.\n", a);

	printf("그렇다면, 500 바이트를 할당 해 봅시다\n");
	c = malloc(500);
	printf("3rd malloc(500): %p\n", c);
	printf("여긴 다른 문자열을 넣도록 하죠.\"this is C!\" 이걸로.\n");
	strcpy(c, "this is C!");
	printf("세번째 할당된 메모리는 %p, 내용은 %s\n", c, c);
	printf("최초 할당된 메모리는 %p, 내용은 %s\n", a, a);
	printf("만약 첫번째 할당을 재사용하게 되면 3번째 할당에 사용된 데이터를 가지게 될겁니다.");
}

'System' 카테고리의 다른 글

How 2 heap - fastbin_dup.c  (0) 2017.05.16
How 2 heap - first_fit.c  (0) 2017.05.15
LLVM -3  (2) 2016.08.24
LLVM - 2  (0) 2016.08.17
LLVM - 1  (0) 2016.08.12
프로그램에서 일어날수 있는 레이스컨디션의 예제  (8) 2015.04.01
Posted by Maid:: IzayoiSakuya

LLVM -3

System 2016.08.24 16:48

지난번 설치까지 끝을 냈다. 그럼 사용을 하는걸로 넘어가 보도록 하자.


1. clang? clang!


처음 설치하고 llvm을 실행해보려고 llvm 파일을 찾았더니 아무리 찾아도 없어서 상당히 당황했다.


여기저기 찾아본 결과, 실행파일명은 llvm이 아니라 clang 이라는걸 알기까지 다소 시간이 걸렸다.


우선 테스트 해볼 소스코드는 아래와 같이 간단하다



쓰이지도 않을 c라는 변수와 함께 HIHIHIHIIH를 출력하고 종료하는 단순한 프로그램이다.


이 프로그램을 각각 gcc와 clang(llvm)으로 컴파일 해 볼 예정이다.


우선 clang을 통해서 컴파일 한 것은 test,

gcc를 통해서 컴파일 한것을 test2으로 지정한 후, 두 파일의 opcodes 를 비교하는걸로 시작하자.


먼저 test ( clang )의 결과물이다.



60 라인( 실제로는 라인 수가 아니지만) 이 출력된 것을 볼 수 있다. 


이제 test2 (gcc)를 보도록 하자.


47 라인으로 끝나는 것을 볼 수 있다. 실제로 아래쪽의 코드가 c언어와 매칭하기 더욱 편한것을 느꼈다.


clang을 통해서 쓰는것이 좀더 쓸데없는? 코드를 더 넣는것 같다.



2. 난독화


llvm을 접하게 되면서 가장 관심이 깊었던건 역시 난독화 분야이다. 이 난독화를 테스트 해보기위해 여기저기 뒤진결과 공식 페이지에 사용방법이 나와있는것을 발견했다.



해당 옵션들을 사용해서 두 파일의 난독화 정도를 비교하기 위해 소스코드를 조금만 더 복잡하게 짜서 테스트 해 보기로 하였다.



코드는 55809와 xor 한 결과값을 두고 문구를 출력하는 간단한 프로그램이다. 분기문이 있어야 될것 같아서 몇가지의 분기문을 추가하고 이 것을 컴파일 해 보았다.


우선 일반적으로 clang을 통해 컴파일 한 결과의 그래프는 아래와 같다



마지막 네모 4개를 보아서 저 부근이 switch 문으로 확인할 수 있다. 크게 복잡하지 않은 구조임을 한눈에 확인 할 수 있다.


이제 모든 옵션을 가미한 결과물의 그래프를 보자.



보기만해도 상당히 암이 걸린다. 위에서 적었다 시피 코드가 크게 복잡하지 않음에도 불구하고 이렇게 암걸리는 코드를 생성해 낼 수 있다.


이걸 NDK에 적용하려고 생각하니 벌써부터 신이 난다


3. 일단 마무리


NDK 설정을 해줘야 하기도하고, 이것을 따로 적용시켜주기도 해야하고.. 다음 포스팅까지는 아마 시간이 길게 걸릴것 같다.



----------------------------------------------------------------------------------------------------------------------------------------------

Reference


NOP


'System' 카테고리의 다른 글

How 2 heap - fastbin_dup.c  (0) 2017.05.16
How 2 heap - first_fit.c  (0) 2017.05.15
LLVM -3  (2) 2016.08.24
LLVM - 2  (0) 2016.08.17
LLVM - 1  (0) 2016.08.12
프로그램에서 일어날수 있는 레이스컨디션의 예제  (8) 2015.04.01
Posted by Maid:: IzayoiSakuya

LLVM - 2

System 2016.08.17 10:35

LLVM 이 무엇인지 알았으니 간략히 설치를 해 보려 한다.


자세한 설치법은.. 역시 공식 사이트를 참조 하는게 가장 좋지만, 그게 귀찮고 힘든 사람들을 위해서 따로 적는것도 나쁘지 않다고 생각한다.


1. LLVM Source Download


16년 8월 17일 현재시간을 기준으로 가장 최근에 올라와 있는 버전은 3.8.1 이다. 하지만 여기서는 Android Obfuscate 를 목적으로 LLVM을 설치하고 있으므로..


3.6.1 버전을 기준으로 설치를 시작한다. 물론, 다른 버전도 설치 방법은 여타 동일하다.


우선은 다운로드 페이지 로 이동해서 3.6.1 버전의 llvm 소스와 clang 소스를 받자.



다른버전을 사용하실 분들께서는 다른버전의 Clang과 LLVM을 받아주시면 된다. 중요한것은, 해당 버전에 맞게 받아 주어야 한다는 것이다.


llvm을 받아서 커스터마이징을 해보고 싶은 분들은 위의 방법과 같이 소스를 받으시면 되고,


난독화를 위해 이미 만들어진 llvm을 사용하시고 싶은 분들은  GIT으로 그 소스가 제공되고 있으며, git clone -b llvm-3.6.1 https://github.com/obfuscator-llvm/obfuscator.git 명령어 입력으로 다운로드 받을 수 있다.


설치방법은 이하 동일하기 때문에 큰 상관이 없다!


2. 설치하기


일단 LLVM의 소스코드를 적절한 디렉토리에 풀고 나면 해당 경로 내부에 tools 라는 폴더가 있다.


이 폴더 안에 Clang의 소스코드를 풀도록 하자.


LLVM

   - Tools

       -Clang

           -Clang의 내용물


의 형태가 되도록 풀어 주어야 한다.

tar 형식으로 되어 있으니 tar -xvf 명령어 등을 사용하여 풀면 된다.


3. 컴파일 시작! 전에


설치 가이드 에서는 cmake를 사용하고 있다. cmake 버전 또한 높은 버전을 요구하므로 cmake의 소스를 받아서 컴파일을 먼저 해 주도록 하자.


여기에서 그 소스들을 다운 받을수 있으며, 간단하게 ./configure 와  make를 실행하는것으로 cmake의 컴파일이 끝난다.


4. 본격적인 컴파일


모든 준비가 되었으면 LLVM 소스코드 폴더 바깥에 임의의 폴더를 하나 만들자. 설치 가이드 에서는 build 라는 폴더로 만들었으니 우리도 친절하게 mkdir build 라고 적어주자.

이제 이 경로에서 cmake -G "Unix Makefiles" ../llvm코드경로 를 입력해 주면 여러 메세지가 나오면서 마지막에 Configure가 완료 되었다고 뜬다.

이후 make 명령어를 입력하여 최종 컴파일에 돌입하자.



컴파일이 쭉쭉 진행되고 있다. 덧붙여서 해당 컴파일은 시간이 상당히 오래 걸리므로 충분히 쉬다가 오셔도 상관 없을것 같다.


-----------------------------------------------

Referer : http://cd80.tistory.com/97

'System' 카테고리의 다른 글

How 2 heap - fastbin_dup.c  (0) 2017.05.16
How 2 heap - first_fit.c  (0) 2017.05.15
LLVM -3  (2) 2016.08.24
LLVM - 2  (0) 2016.08.17
LLVM - 1  (0) 2016.08.12
프로그램에서 일어날수 있는 레이스컨디션의 예제  (8) 2015.04.01
Posted by Maid:: IzayoiSakuya

LLVM - 1

System 2016.08.12 13:44

0. Intro


주변에서 LLVM에 대한 소문이 무성히 들려온다.


이런저런 사정으로 LLVM을 손대봐야 하는 상황까지 와버려서, 설치부터 실행, 응용까지 차근차근 겪은일들, 해결한 방법 등을 적어 나가려 한다.


우선 LLVM이 무엇인지 알아야 하는데.. 검색으로도 LLVM이 무엇인지 알수 있다.


Low Level Virtual Machine 이라고 하는데.. 뭐 구구절절한건 집어치우고 일단 본론만 들어가자.



1. 왜 LLVM인가?


일단 LLVM이 무슨 프로그램인가 하고 굳이 분류를 나누자면 컴파일러 인 것 같다. 이런저런 글들을 다 살펴보아도 컴파일러에 관한 이야기 밖에 없다.


그럼 평소에 우리가 쓰는 gcc와 같은 컴파일러와 무엇의 차이점이 있는것인가?


이는 어쩔수 없이 흔한 그림으로 대체하여 설명하려 한다.


다소 거부감이 있더라도 최대한 이해한대로 쉽게 풀어 쓸 테니 양해하고 읽으면 좋겠다.


 

출처 : http://kyejusung.com/


글쓴이는 컴파일러에 대한 공부를 한 적이 없다. 그래서 알고있는것은 gcc source.c -o output 정도.


내가 저렇게 명령어를 입력하고 gcc를 실행시키면, 내부적으로는 위의 그림처럼 3단계를 거쳐서 ouput이 나온다고 한다.


모두가 알다시피 우리가 사용하는 프로그래밍 언어는 컴퓨터가 알아듣지 못한다. 이를 기계어로 번역하는 일을 컴파일러가 도맡아 하는데, 이때 몇가지의 과정이 있다.



- Frontend 

 해당 단계에서는 우리가 쓴 프로그래밍 언어를 분석하고 중간코드를 생성한다. LLVM에서는 중간코드를 IR 이라고 부르는 모양이다.


-Optimizer 

 단계 이름 그대로 최적화를 위한 중복계산을 제거하거나 여러가지 변환을 실행한단다.


-BackEnd

 코드를 타겟에 맞는 기계명령어로 매핑하고 실행코드를 생성한다. 



이렇게 3단계를 거쳐서 우리는 output 이라는 컴파일 결과물을 얻을수 있다.


물론, 우리가 자세히 알필요는 없었다. 우리가 필요한건 결과물이지 그 중간과정이 필요한게 아니었으니까. 내가 명령어를 넣으면, 결과물이 나온다. 

이게 글쓴이가 알고있던 컴파일러였다.


LLVM은 쓸데없이 이 3단계를 따로 분리 해 둔 컴파일러이다.


출처 : http://kyejusung.com/



나올 결과물만 보면 이전의 그것들과 다를것이 없다. 우리는 평범하게 소스코드 파일을 입력으로 줄것이고, LLVM은 이것을 컴파일해서 결과물을 가져올 것이다.


이제 겉은 그만 보고 조금만 안으로 들여다보면, LLVM의 잔머리에 감탄할 수도 있다.


LLVM은 중간코드(이하 IR)를 만들고 이를 각 아키텍쳐(타겟)에 맞는 명령어 셋으로 변환 할수 있다.


더이상 크로스컴파일 같은것에 얽매일 필요가 없다는 뜻이다.


사실 이 내용을 처음 접했을때는 여러가지 크로스 컴파일을 덕지덕지 붙여논것이라고 생각했다. 하지만 여기서 LLVM은 한술 더 뜬다


아키텍쳐에 맞는 명령어셋을 IR코드로 변환 가능하다


IR을 명령어로, 명령어를 IR로 바꿀수 있는것은 아마 생각하지 못한 여러가지 이점을 가져올수 있다. 악성코드 분석이든, 개발이나 어느방향이든.



개인적인 사정을 떠나서 LLVM이 어떻게 동작할지, 어떻게 다룰수 있을지에 대해서 흥미가 붙었다.


다음 해야할 일은 LLVM을 설치하고.. 이것을 어떻게 사용할지, IR은 어떻게 보는지, 그리고 정말 결과물을 내가 IR코드로 되돌릴수 있는지에 대해서 테스트 해 볼 생각이다.

'System' 카테고리의 다른 글

How 2 heap - fastbin_dup.c  (0) 2017.05.16
How 2 heap - first_fit.c  (0) 2017.05.15
LLVM -3  (2) 2016.08.24
LLVM - 2  (0) 2016.08.17
LLVM - 1  (0) 2016.08.12
프로그램에서 일어날수 있는 레이스컨디션의 예제  (8) 2015.04.01
Posted by Maid:: IzayoiSakuya

구글에 레이스 컨디션 이라고 검색하니 공격기법만 난무해서, 간단한 예제와 함께 포스팅을 시작하려 합니다.


우선 레이스 컨디션이란 단어에 대한 뜻은 위키 백과에 나와 있는 부분을 가져왔습니다.



전산학에서 경쟁 상태란 공유 자원에 대해 여러 개의 프로세스가 동시에 접근을 시도하는 상태를 말한다. 동시에 접근할 때 자료의 일관성을 해치는 결과가 나타날 수 있다. 이를 방지하기 위해서는 프로세스 협력 기법이 필요하다. 

-http://ko.wikipedia.org/wiki/경쟁_상태


사실 프로세스가 아니더라도 경쟁상태에 있을수 있는것은 수많은 것들이 있습니다. 그것은 스레드가 될수도 있고, 다른 어떤 자원이 될수도 있겠죠!


사실 이러한 레이스 컨디션을 이야기 하기전에 조금 다른 이야기 일수 있는 fork, system, execl, thread 등의 프로세스 내부에서 다른 프로세스나 스레드를 생성하는 것들에 대해서 이야기 해봅시다.


복잡한 코딩없이 시스템 함수를 실행시키기 편한 system의 예제부터 보도록 하죠.


system.c

#include <stdio.h>

#include <unistd.h> int main() { printf("I'm Parent!\n"); system( "./call_exec"); printf("is over!\n"); }


call_exec.c

#include <stdio.h>
int main()
{
        int i;
        printf("I'm child!\n");
        for(i =0; i <100; i++)
                printf("now.... %d\n",i);
        while(1)
        {}
}



system.c와 call_exec를 컴파일 하고 돌려봅시다.

물론, call_exec의 무한반복루프 때문에 프로그램이 종료되지 않은채 살아 있을겁니다.

컨트롤+c 키를 이용해서 무한반복문을 빠져나와 보죠.


맨 마지막의 is over! 도 출력이 정상적으로 되는걸 볼 수 있습니다. 예상했던 결과대로 프로그램이 흘러가는군요.

system은 내부적으로 결과값을 리턴하기 전까지 부모 프로세스가 죽지 않았다는걸 알수 있습니다.

이제는 일반적인 pwnable 환경에서 자주 쓰이는 execl 등의 함수를 알아보도록 하죠.


예제코드는 아래와 같습니다.


exec.c

#include <stdio.h>

#include <unistd.h> int main() { printf("I'm Parent!\n"); execl( "./call_exec", "./call_exec", NULL); printf("is over!\n"); }

 


두번째 인자값은 argv[0]로 들어가는 인자값이라고 합니다. argv[0]는 자신이 실행된 경로가 적힌 arg로,

int main(int argc, char** argv)를 사용하셔서 확인하실수 있습니다.

이제 이것을 컴파일하고 실행시켜보죠.


예상과는 다르게 execl이 프로그램을 실행시키든 말든 is over!라는 문자열을 출력해주지 않는군요.

왠지 바이너리 자체가 다른걸로 교체된것 같은 느낌을 강하게 받습니다. 그럼 fork는 어떨까요?

fork.c

#include <stdio.h> #include <stdlib.h> int global_secret_number; int main() { int main_secret_number = 1; global_secret_number = 3; int pid; pid = fork(); if(pid>0) { main_secret_number++; global_secret_number++; printf("I'm Parent!\n main: %d global: %d\n",main_secret_number,global_secret_number); printf("PID %d\n",pid); } else { printf("I'm child!\n main: %d global: %d\n",main_secret_number,global_secret_number); printf("PID: %d\n",pid); } }


소스가 이전것들과 비해 좀 복잡한 느낌이 있습니다. fork는 부모의 프로세스를 그대로 '복사' 하여 한번 더 실행한것과 같은 효과를 가집니다.

부모와의 차이를 두기위해 pid로 자식/부모를 구분하며, pid가 0이면 자식, 0 이상이면 부모으로 판별하는 방법을 주로 사용합니다.

여기서 복사 하였다는 표현을 사용했는데, 아래 결과를 보면 어느정도 짐작할 수 있습니다.



전역변수로 사용한 global의 값을 자식프로세스에서 +1을 더했지만, 부모프로세스에서는 변화가 없는걸 보실수 있습니다.

이것은 fork를 하면서 모든 메모리 영역을 똑같이 복사해서 자식만의 메모리를 가졌기 때문입니다.

(똑같은 행동을 하는 프로세스를 하나 더 실행했다고 생각하시면 이해가 편하실겁니다.)


그럼 이쯤에서 레이스 컨디션의 이야기로 넘어가 보도록 하죠.

우선 아래와 같은 상황을 가정해봅시다.


1. 두 프로세스가 공통된 파일에 접근한다.

2. 하나의 프로세스에 있는 두개의 쓰레드가 공통된 파일에 접근한다

3. 부모 프로세스가 값을 생성하고, 그 값을 자식 프로세스가 가져와 사용하려한다.


이러한 상황에서 발생할수 있는 문제가 있을까요?

우선 간단한 thread를 통한 예제를 살펴봅시다.


thread.c

#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <time.h> #define bool char #define STDOUT 1 bool islock; bool outlock; bool inlock; int money; void* input(void* arg) { char buf[50] = {0,}; int i; //for busy waiting while(islock && inlock) {} outlock = 0; for(i =0; i<100; i++) { int len; len = sprintf(buf,"INPUT MONEY : %d\n",money); money += 100000; printf("%s",buf); } } void* output(void *arg) { char buf[50] = {0,}; int i; //for busy waiting while(islock && outlock) {} inlock =0; for( i =0; i <100; i++) { int len; len = sprintf(buf,"OUTPUT MONEY : %d\n",money); money -=100000; write(STDOUT,buf,len); } } int main() { money = 100000; pthread_t thread[2]; islock= inlock= outlock= 1; //Do input! printf("THREAD MAKE!\n"); pthread_create(&thread[0],NULL,&input,NULL); pthread_create(&thread[1],NULL,&output,NULL); sleep(1); islock = 0; sleep(1); }



우선 input과 output의 함수의 속도 차이를 주기 위해서 하나는 저수준의 입출력을, 하나는 단순 printf문을 사용해서 프로그램을 설계해보았습니다. 물론, 저수준의 입출력이 보통의 printf보다 약간은 더 빠르겠죠?

그리고 예상되는 결과를 생각하시고 컴파일후 실행해 보시길 바랍니다.

제가 실행해 보았을때는 아래와 같이 2군데 에서 이상한 점을 찾을 수 있었습니다.




쓰레드(프로세스)를 만들고 나서 OUTPUT MONEY에 돈이 100000 증가한 점,

그리고 OUTPUT MONEY 이후에 INPUT MONEY의 금액이 갑자기 급증한 점.

과연 관계가 있을까요?


여기서 예상가능한 상황은 다음과 같습니다.


1. OUTPUT MONEY 가 동작한 이후, INPUT MONEY가 동작하면서 전역변수 money의 값이 변경되었다.

2. 하나의 전역변수 money에 대해서 두개의 쓰레드(프로세스)는 각자 다른값을 가지고 자신이 취해야 하는 행동을 했다.

3. 그 결과, 100000이 유지되어야 할 전역변수 money의 값이 서로 다르게 책정되었고, 원하지 않는 결과를 가져왔다.


그림으로 나타내면 이렇게 되겠죠!




이게 레이스 컨디션입니다.

OUTPUT 함수가 실행될 동안 어떠한 함수나 프로세스, 쓰레드는 전역변수 money에 접근해서는 안되며,

INPUT 함수가 실행될 동안 어떠한 함수나 프로세스, 쓰레드는 전역변수 money에 접근해서는 안된다 라는 원칙을 정해주지 않았기 때문에 

서로가 가지고 있는 값이 다르게 된 것이죠.


이 예시를 좀더 와닿을수 있는 예제 코드를 준비해 보았습니다.


bank.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h> // for logging!

#define ACCOUNT "ACCOUNT"
#define LOG "LOG"
#define DEPOSIT 1
#define DRAW 2
#define ASK 3

//is not important in this example. it just write log.
void WriteHistory(unsigned int menu, unsigned int amount, unsigned int money)
{
	FILE* log = fopen(LOG,"a");
	char action[10] = {0,};
	char buf[100] = {0,};
	time_t current;
	time(&current);

	fwrite("--------------------------------\n",1,33,log);
	switch(menu)
	{
		case DEPOSIT:
			sprintf(buf,"MONEY :: %d (DEPOSIT :: %d) \n TIME :: %s\n\n",money,amount,ctime(&current));
			break;
		case DRAW:
			sprintf(buf,"MONEY :: %d (DRAW :: %d) \n TIME :: %s\n\n",money,amount,ctime(&current));
			break;
		case ASK:
			sprintf(buf,"ACCOUNT ASK \n TIME :: %s\n",ctime(&current));
			break;
	}
	fwrite(buf,1,strlen(buf),log);
	fclose(log); //LOG EXIT
}

void clear()
{
	system("clear");
}
void printmenu()
{
	clear();
	printf("+--------------------------------+\n");
      	printf("|     WELCOME TO SAKUYA ATM      |\n");
        printf("+--------------------------------+\n");
	printf("|[*]HERE IS YOUR MENU            |\n");
        printf("+--------------------------------+\n");
        printf("|1. Deposit                      |\n");
        printf("|2. Draw                         |\n");
        printf("|3. Ask my account               |\n");
        printf("+--------------------------------+\n");
        printf(">> ");

}

FILE* openaccount(char* mode)
{
	FILE* ret;
	while(1)
	{
		ret = fopen(ACCOUNT,mode);
		if(ret>0)
			return ret;
	}	
}

void deposit()
{
	//OPEN MY ACCOUNT
	FILE* Raccount = openaccount("r");
	unsigned int money;
	char buf[10] = {0,};
	unsigned int input;
	fscanf(Raccount,"%d",&money);
	fclose(Raccount);	//this file no more use for read mode. aren't you?

	//DO Deposit
	clear();
	printf("How much depoist in your account?\n>> ");
	scanf("%d",&input);
	
	money += input;

	sprintf(buf,"%d",money);
	
	FILE* account = openaccount("w");
	fwrite(buf,1,strlen(buf),account);
	fclose(account);
	
	//logging
	WriteHistory(DEPOSIT,input,money);	
}

void draw()
{
	//OPEN MY ACCOUNT
	FILE* Raccount = openaccount("r");
	unsigned int money;
	char buf[10] = {0,};
	unsigned int output;
	fscanf(Raccount,"%d",&money);
	fclose(Raccount);	//this file no more use for read mode. aren't you?

	//DO Draw
	while(1)
	{
		clear();
		printf("Your account has %d won!\n",money);
		printf("How much draw in your account?\n>> ");
		scanf("%d",&output);
		if(money < output)
			continue;
		else
		{
			money -= output;
			break;
		}
	}
	
	sprintf(buf,"%d",money);	//for fwrite!

	//OPNE MY ACCOUNT to write my money
	FILE* account = openaccount("w");
	fwrite(buf,1,strlen(buf),account);
	fclose(account);

	//logging
	WriteHistory(DRAW,output,money);
}


void ask()
{
	WriteHistory(ASK,0,0);
	clear();
	system("cat ./LOG");
	printf("For security reason, we will show this log 5 second.\n");
	sleep(5);
}

int main()
{
	int sel;
	while(1)
	{
		printmenu();
		scanf("%d",&sel);
		switch(sel)
		{
			case DEPOSIT:
				deposit();
				break;
			case DRAW:
				draw();
				break;
			case ASK:
				ask();
				break;
		}
	}	
}


소스가 좀 길군요. 실행화면을 가져와 볼께요.


간단한 ATM 기계네요!

입금, 출금을 할수 있고 내 계좌에 대해 조회도 할 수 있습니다.

우선 간단히 입금과 출금 기능을 살펴 볼게요.

아,참 이 프로그램은 관리자만 ACCOUNT에 접근해야 하므로 setuid를 걸어놓았습니다. 아무래도 그게 더 현실적이니까요 :D



우선 입금 기능을 살펴보도록 하죠.

원하는 액수만큼 돈을 넣으시면 됩니다.

간단하죠?



출금기능에선 내가 가진 잔고를 보여주고 그 잔고 이상을 출금하려고 하면 출금이 안됩니다.



그리고 계좌조회 기능에서는 내가 어떤행동을 언제 했는지 친절히 보여주는 프로그램입니다. 전체적으로 간단한 기능을 가진 프로그램들이죠.


여기서 레이스컨디션이 일어날수 있을까요?


우선 이 프로그램을 2개 켜보도록 합시다.그리고 돈을 출금해보도록 하죠.

그런데, 정말 우연히도,

제 계좌에 누군가가 돈을 송금했습니다. 그것도 무려 1000만원이나 되는 금액을요!

제가 계좌에서 돈을 뺴 가기도 전에 말이죠!

그림으로 보면 다음과 같은 상황일겁니다


돈이 필요해서 20만원을 모두 빼려는데,


누군가 저에게 천만원을 입금해 줬고!


그 금액이 그대로 들어가서 돈이 총 1020만원이 되었지만


제 계좌에는 0원이 남아있군요!


순서대로 정리를 하면, 



1번 상황에서 제가 가지고 있는 돈은 200000입니다. 

그리고 2번 상황이 오죠 (입금). 여기서도 물론 제가 가지고 있는 돈은 200000입니다.

3번처럼 입금이 완료되고, 원래 흐름이었던 출금으로 돌아왔을때 기억하고 있는 금액은 200000이죠. 입금이 더해진 금액이 아닙니다.


그리고 출금을 완료하고 기록을 하게되면, 금액은 0원이 되겠죠.


이것 또한 제 계좌에 대해서 한번에 2개이상의 프로세스가 접근했기 때문에 발생하는 것입니다.

제가 출금을 하고있을때 다른분이 제 계좌에 접근하지 못했다면, 좀더 정확히는 제 계좌의 돈이 동기화가 됬다면 이 문제는 발생하지 않았겠죠.


사실 이러한 문제를 일으키는 코드들이 도처에 널려있을지도 모릅니다.



------------------------------------------------

int -> unsigned int :: thx to chofly 

설명을 위한 그림추가

fork 관련 내용 수정 :: thx to bongbong

'System' 카테고리의 다른 글

How 2 heap - fastbin_dup.c  (0) 2017.05.16
How 2 heap - first_fit.c  (0) 2017.05.15
LLVM -3  (2) 2016.08.24
LLVM - 2  (0) 2016.08.17
LLVM - 1  (0) 2016.08.12
프로그램에서 일어날수 있는 레이스컨디션의 예제  (8) 2015.04.01
Posted by Maid:: IzayoiSakuya

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.