본문 바로가기

High Level Technique

DLL Injection DLL Injection DLL Injection은 프로그램이 실행되는데 DLL 파일이 함께 사용되는데, 이 프로그램에 DLL 파일을 삽입하여 사용하는 것을 말한다. * DLL (Dynamic Linked Library)DLL은 동적 라이브러리라고 불리며, DLL이 로딩되면 DllMain()이 실행된다. DLL Injection이 활용되는 곳은 후킹, 기능 개선, 버그 패치 등등 여러 일을 할 수 있다. CreateRemoteThread() CreateRemoteThread()는 Jeffrey Ritcher에 의해서 공개되었다고 합니다. hProcess : Thread가 생성될 타겟 프로세스, Thread 생성에 성공하기 위해서는 이 Handle에 대해 PROCESS_VM_OPERATION, PROCES.. 더보기
Bypass Canary Bypass Canary 개념 BOF가 발생할 수 있는 환경이지만 buffer를 모두 채운 뒤에 다음 4바이트가 Canary로 채워져 있어 해당 값이 변경이 될 때 에러를 출력하면서 프로그램이 종료되는데, 이 Canary를 유추해서 우회를 하는 방법을 말한다. 설명 1. Brute Force buffer를 모두 채운 후 다음 4바이트가 Canary로 채워져 있는데 1바이트를 넘어가도록 buffer를 채운다.그러면 Canary 영역에 1바이트가 침범하여 값이 다를 경우 에러를 출력하는데 이때 이 1바이트를 0x00 ~ 0xFF(256)까지 모두 넣어서 뜨지 않는 경우를 찾아낸다. 이렇게 4바이트를 모두 찾아낸다. 2. recv, strncpy recv와 strncpy 함수는 문자열을 입력받을 시 NULL이.. 더보기
Double Stage Format String Bug (DSFSB) Double Stage Format String Bug (DSFSB) 개념 일반적으로 Format String Bug(이하 FSB)는 스택의 값을 조작하여 원하는 결과가 나타나도록 공격하는 방법 이지만. buffer가 전역으로 선언되어있고 스택에 존재하는 값을 변조할 적절한 값이 없을 경우 사용한다. 설명 기본적인 FSB는 변조하고 싶은 주소(got)를 넣고 해당 값이 나오는 곳에 %n 지시자를 이용해서 실제 실행하고 싶은 주소를 넣어 실행시키도록 하는 것이죠.하지만 buffer가 전역으로 선언되어있으면 변조하고 싶은 주소(got)를 조작할 수 없게 됩니다. 이번 설명은 pwnable.kr의 fsb 문제를 이용하여 설명하도록 하겠습니다. fsb 바이너리를 gdb를 이용해서 확인해보면 fsb라는 함수가 존.. 더보기
Use After Free (UAF) Use After Free (UAF) 개념 프로그래머가 코드를 작성시 실수를 하여 발생하는 것을 말한다. Heap 영역에 할당하여 사용 후에 free를 하면 해제가 되는 것이 맞지만 같은 크기로 다시 할당하는 경우 한번 할당된 영역을 다시 재사용 됨으로써 발생하는 것을 뜻한다. 브라우저 익스플로잇을 할 때 많이 사용된다. 설명 먼저 간단한 C 코드를 보면서 UAF가 어떻게 발생 되는지 알아보도록 하겠습니다. test라는 구조체에는 int형 변수가 선언되어 있습니다. 그리고 main()에서는 p1, p2로 구조체 포인터로 선언이 되있습니다. p1과 p2는 각각 동적할당으로 256만큼 할당하도록 되어있죠. 프로그램이 동작하는 순서를 보면, p1이 동적할당되고 초기화가 안된 num의 값을 가져옵니다. 그리고 .. 더보기
SigReturn Oriented Programming (SROP) SigReturn Oriented Programming (SROP) 개념 Return Oriented Programming (ROP)의 변종으로서 system call를 이용한 기법으로 가젯이 충분하여 register를 원하는대로 조작할 수 있으면 상관 없으나 그렇지 않은 경우 사용하는 방법이다. SROP는 가젯이 많이 필요하지 않고 INT80 가젯만 있으면 가능한 기법이다. 설명 SigReturn Oritented Programming(이하 SROP)는 sigreturn을 이용하여 쉘을 따내는 기법을 말합니다.sigreturn은 syscall table을 통하여 찾아보게 되면 아래와 같이 나타납니다. eax값으로 119를 넣어주면 sigreturn이 실행이되는 것이죠. 그리고 sigreturn의 파라미.. 더보기
Return Oriented Programming (ROP) - final Return Oriented Programming (ROP) - final 목표 ROP를 stage0, 1로 나누어 정확한 개념을 통해 이해해 보자. 설명 이번에 설명할 것은 step1, step2를 통해서 ROP를 어떻게 수행하는지에 대해서 알아봤습니다.이번 step3에서는 좀 더 ROP에 대해서 자세하게 알아보도록 하겠습니다. ROP는 stage0, 1에 따라서 실행을 합니다. 이번에 사용할 바이너리를 받아주세요. shayete 형 ㄱㅅ 해당 바이너리를 사용하는 법은 ./rop port 입니다. 해당 바이너리에는 아래와 같은 취약한 부분이 있습니다. vuln이라는 배열에는 100만큼의 공간이 있지만 recv()를 통해서 200만큼 받을 수 있게 해두었습니다. 따라서 BOF가 발생할 수 있습니다. 먼저.. 더보기
Return Oriented Programming (ROP) - step 2 Return Oriented Programming (ROP) - step 2 목표 L.O.B FC3 evil_wizard를 이용하여 ROP를 socket을 이용하여 어떻게 페이로드를 작성하는지 알아보도록 하자. 설명 위 C 코드는 LOB FC3 evil_wizard 입니다. 해당 문제의 주석을 확인해 보면 8888포트로 접속하여 하라고 합니다. 먼저 공격 방법을 생각해 봅니다. step1에서도 system("/bin/sh")를 만들어서 사용했었습니다. /bin/sh를 .bss영역에 넣어뒀었죠. system() 주소, /bin/sh 주소, pop pop ret 주소 strcpy() 주소 를 찾아야 합니다. .bss 주소 .bss section의 주소는 0x8049868 입니다. strcpy() 주소 str.. 더보기
Return Oriented Programming (ROP) - step 1 Return Oriented Programming (ROP) - step 1 개념 Return Oriented Programming(이하 ROP)는 운영체제의 보호기법인 NX, PIC, PIE, ASLR을 우회하여 해커가 원하는 명령어를 가지고 실행할 수 있도록 조합하여 권한 상승을 하는 기법.쉽게말해서 이것 저것 제약이 걸린 상태에서 BOF, RTL과 같은 기법들이 수행되지 못하는 경우 원하는 함수를 실행하기 위해 필요한 값들을 하나하나 찾아 조합하여 실행시킬 수 있는 방법을 뜻합니다. 설명 ROP는 해커가 제약된 환경에서 쉘을 얻어내기 위해 조각들을 모아 실행 시키는 것을 말합니다. 간단한 예제를 통해서 어떠한 방식으로 ROP가 이루어지는지 살펴보도록 하겠습니다. 해당 소스코드를 보면 main에서는 .. 더보기
Return to Library Chain (RTL Chain) Return to Library Chain (RTL Chain) 개념 Return to Library Chain(이하 RTL Chain)은 BOF가 일어나는 취약한 프로그램에서 사용하고자 하는 라이브러리 함수들을 계속해서 호출하여 사용할 수 있는 기법.Rreturn Oriented Programming의 기본 바탕이 된다. 설명 RTL Chain은 RTL을 연속적으로 사용하여 원하는 함수를 사용하는 것을 말합니다. 간단한 예제를 통해서 알아보도록 하겠습니다. 위 C 코드는 main에서 buf의 16개의 공간에 argv[1]을 통해서 값을 저장시키는 함수입니다. 그러나 func1, func2, func3의 함수들은 실행을 할 수 없죠.하지만 앞서 공부한 내용들을 바탕으로 buf + SFP + Ret 구조라.. 더보기
Format String Bug (FSB) Format String Bug (FSB) 개념 Fortmat String Bug(이하 FSB) 는 printf()를 사용할 때 프로그래머의 실수로 인해 배열 안의 값을 printf("%s", buf)로 사용하는 것이 아니라 printf(buf)로 사용함에 있어서 나타나는 버그를 말한다. printf(buf)와 같이 소스코드를 작성하여 실행할 경우 buf안에 문자를 넣는 것이 아니라 서식문자, 지시자를 넣었을 경우 스택에 쌓여있는 값을 출력하는 버그를 발생한다. 설명 먼저 Format String에 대해서 알아보도록 하겠습니다. Format String은printf("num is %d \n, num"); 과 같이 작성된 코드에서 "num is %d \n"를 말합니다. 쉽게 생각하면 printf()안에서 .. 더보기
Return To Library (RTL) Return To Libray (RTL) 개념 NX Bit(Never Execute Bit)가 적용된 스택을 우회하기 위해서 사용되는 기법. * NX BitNX Bit는 프로세스 명령어나 코드 또는 데이터 저장을 위한 메모리 영역을 따로 분리하는 CPU의 기술. 지정된 모든 메모리 구혁은 데이터 저장을 위해서만 사용되며, 프로세서 명령어가 그 곳에 상주하지 않음으로써 실행되지 않도록 만들어준다. 설명 메모리 보호기법(DEP)의 NX Bit를 우회하여 사용하는 방법이 RTL입니다. 먼저 RTL에 대해서 설명하기 전에 알아야 할 개념이 Plt와 Got 입니다. http://kblab.tistory.com/217 RTL은 Plt와 Got를 이용하여 쉘을 실행시키는 방법입니다. 위 C 코드를 보면 argv로 받.. 더보기
Plt 와 Got Plt와 Got 개념 PLTProcdedure Linkage Table의 약자로 프로시져들을 연결해 주는 테이블을 말합니다. 프로그램을 어떻게 구현 했느냐에 따라 달라지기도 합니다.실제 호출 코드를 담고 있으며 이 내용을 참조해 _dl_runtime_resolve가 수행되고, 실제 시스템 라이브러리 호출이 이루어 집니다. GOTGlobal Offset Table의 약자로 함수들의 주소를 담고 있는 테이블입니다. 라이브러리 함수를 호출할때 PLT가 GOT를 참조하게 됩니다.첫 호출시엔 코드의 영역의 벡터 주소가 들어가 있고, 두번째 호출부터 그 함수의 실제 주소가 들어가져 있다. 설명 위 C 코드를 보면 매우 간단하게 printf와 puts를 사용하고 있습니다. 그저 단순 출력 소스코드 입니다. 해당 파일.. 더보기
Fake EBP Fake EBP 개념 변조시킬 Return Address에 \xbf(스택), \x40(라이브러리) 주소를 사용할 수 없을 때, Return Address 이후에 사용할 만한 공간이 없을 때 사용하는 방법.Save Frame Pointer Overwrite처럼 SFP를 변조하여 메모리의 다른 부분을 프로그램의 일부분 처럼 실행하도록 하는 기법입니다. 설명 위 C 코드는 LOB의 assassin 소스코드 입니다. strncpy를 이용해서 총 48자리까지 buffer에 값을 넣을 수 있습니다. 48까지라는 것은 buffer의 크기 40 + SFP 4 + RET 4까지 덮어 쓸 수 있다는 이야기입니다. gdb를 통해서 분석을 해보도록 하겠습니다. 일단은 r `python -c 'print "a"*40 + "AA.. 더보기
main의 SFP는 왜 0이 되는가? main의 SFP는 왜 0이 되는가? 목적 main함수 외 프로그래머가 만든 함수에 진입 시 ebp의 값을 저장하기 위해 SFP 공간에 ebp를 저장되는 것을 알 수 있다.하지만 main함수 또한 libc_start_main에서 넘어와 main함수가 시작 되는데, 그러면 main의 SFP에도 libc_start_main에서 사용된 ebp가 저장되어야 하지 않을까?라는 의문이 든다. 왜 main함수에서의 SFP의 값이 0이 되는지에 대해 분석해 보았다. 탐구 위와 같이 아주 간단한 C 코드를 작성하여 -fno-stack-protector -mpreferred-stack-boundary=2 옵션을 사용하여 컴파일 후 gdb로 분석을 합니다. gdb를 이용하여 실행 되기 전 i func 명령어를 통해서 함수들.. 더보기
Save Frame Pointer Overwrite (SFO) Save Frame Pointer Overwrite (SFO) 개념 Buffer Over Flow (이하 BOF, bof)가 일어나는 프로그램 취약점을 이용하여 Save Frame Pointer를 덮어씌워 원하는 주소로 이동하는 기법. 설명 앞서 BOF에 대해서 공부를 하셨다면 SFP Overwrite도 쉽게 이해 할 수 있을 것입니다. SFP는 Save Frame Pointer로 이전 함수의 ebp를 저장해 두어 함수의 실행이 끝나고 이전 함수로 돌아가기 위해 저장해두는 역할을 하게 됩니다. 위 C 코드를 보면 main 함수에서는 출력과, sub_func()을 호출합니다.sub_func에서는 buf가 8개의 공간이 할당 되어 있고, scanf를 이용하여 buf에 값을 저장합니다. 앞서 BOF의 포스팅 .. 더보기
Buffer Over Flow (BOF) Buffer Over Flow (BOF) 개념 Buffer Over Flow (이하 BOF, bof)는 정해진 공간에 정해진 만큼 값을 넣어야 하지만 "크기 제한"을 하지 않아 정해진 공간 보다 더 값을 넣을 수 있는 취약점을 말합니다. 가령, buf[10]이라는 배열을 만들었다면 buf라는 배열은 총 10개의 공간을 갖게 됩니다. 하지만, 프로그램이 실행 되면서 buf에 10개의 값이 아닌 11개, 12개 혹은 그 이상의 값이 들어가는 것을 확인하지 않고 처리함 으로서 발생하게되는 취약점을 뜻합니다. bof는 stack에서 발생하기 때문에 stack corruption 으로도 불립니다. 설명 위 소스코드를 보면 매우 간단한 프로그램입니다. buf라는 char형 배열은 총 8개의 공간을 갖고 있고, sc.. 더보기
Linux Forensic 정리 보호되어 있는 글입니다. 더보기
정리 보호되어 있는 글입니다. 더보기
Insertion Encoder Insertion Encoder 이번에는 Insertion Encode에 대해서 알아보도록 하겠습니다. Insertion이라는 뜻은 삽입이라는 뜻입니다. 즉, 인코딩을 하는데 임의의 값을 삽입하여 인코딩을 한다는 것이죠.임의의 쉘 코드에 임의의 값을 사이사이에 넣어서 인코딩하는 방식입니다. 마찬가지로 execve 소스코드를 가지고 해보도록 하겠습니다. objdump를 이용해서 셀 코드를 만듭니다. 해당 셀 코드를 Insertion Encoding을 하도록 하겠습니다. 해당 python 소스코드를 이용해서 쉘 코드 사이사이에 0xAA라는 값을 넣는 프로그램입니다. 해당 python을 실행하면 헥스값 사이사이에 0xaa값이 들어갔습니다. 디코더 만들기 컴파일 후 objdump를 이용해 쉘 코드를 만듭니다. .. 더보기
NOT Encoder NOT Encoder 이번에는 XOR에 이어서 NOT Encoder를 만들어 보도록 하겠습니다. 마찬가지로 execve 소스코드를 가지고 하도록 하겠습니다. objdump를 이용하여 만든 쉘 코드를 Not Encoding하기 위해서 다음과 같이 python 소스코드를 작성합니다. 해당 python 소스코드를 작성 후 실행하면 다음과 같은 결과가 출력됩니다. 디코더 만들기 not_encoding을 한 쉘 코드를 위와 같이 디코더를 만든 후 컴파일 합니다.마찬가지로 쉘이 실행되는지 확인하기 위하여 테스트를 합니다. 더보기
XOR Encoder XOR Encoder XOR 연산을 이용한 인코딩을 해보도록 하겠습니다. 인코딩을 하는 이유는 눈에 쉽게 보이지 않기 위해서 사용됩니다.앞서 소스코드를 컴파일해서 objdump를 이용하여 만든 쉘 코드를 XOR 인코딩을 해서 사용하는 방법입니다. 소스코드 작성하기 앞서 포스팅한 execve shellcode를 가지고 XOR Encoding을 해보도록 하겠습니다. objdump를 이용해서 쉘 코드를 만듭니다. 해당 쉘 코드를 복사하여 python을 이용해서 xor 인코딩 합니다. 3줄의 shellcode의 튜플에 값을 넣어줍니다.소스코드를 보면 0xAA로 해당 쉘 코드를 인코딩합니다. 실행 시 다음과 같이 출력됩니다. 디코더 만들기 XOR 인코딩을 통해 만든 쉘 코드를 위 디코더 소스코드에 넣어줍니다.소스.. 더보기
execve shellcode execve shellcode 본격적으로? 쉘을 실행시켜보도록 하겠습니다. execv를 이용하여 /bin/sh를 실행하는 쉘 코드를 만들어보도록 하겠습니다. execve의 인자 값은 다음과 같습니다.int execve(const char *filename, char *const argv[], char *const envp[]) 입니다. filename에 /bin/sh가 들어가고, argv[]에 /bin/sh의 주소를 넣고, envp[]에 널을 넣습니다. 소스코드 작성하기 x86 eax값에 11을 넣고 pop을 이용해 스택에 저장된 /bin/sh의 주소를 가져옵니다. ecx에 널을 넣고 커널에 넘겨줍니다. 컴파일 후 실행하면 정상적으로 쉘이 실행된 것을 확인할 수 있습니다. 소스코드 작성하기 x64 x86.. 더보기
RIP 상대주소 이용하기 RIP 상대주소 이용하기 지금까지 소스코드들이 대부분 x86를 이용했는데요. 이번에는 x64를 이용하도록 하겠습니다.물론, 이 포스팅에서 사용되는 소스코드는 x64에서만 가능합니다. 소스코드 분석 중요 부분만 분석하도록 하겠습니다. 나머지 부분들은 다 이해 할 수 있을거라 생각합니다. 이해가 되지 않는다면 댓글을 남겨주세요. 12줄 : rip와 상대주소를 이용하여 rsi에 주소를 넣습니다.※ 상대주소를 사용하는 방법은 [rel]를 사용하는 것입니다. rel를 사용하면 rip와 상대주소를 rsi에 저장합니다. GDB로 분석을 스스로 해보시길 바랍니다. 더보기
JMP-CALL-POP 기술 JMP-CALL-POP 기술 점프, 콜, 팝 기술은 매우 중요합니다. 중요한 이유는 자신이 작성한 코드를 실행시킬 수 있기 때문이죠. 앞 포스팅에서 HelloWorld가 실행되지 않았는데, 이 것을 JMP-CALL-POP을 이용해서 실행이 되도록 해보겠습니다. JMP-CALL-POP 변경 JMP-CALL-POP를 이용하여 소스코드를 수정했습니다.왜 이러한 방식으로 소스코드를 작성 했을까요? 소스코드 분석을 해보도록 하겠습니다. 5 : call_shellcode로 점프합니다.25: shellcode를 호출합니다.※ 왜 jmp를 이용해서 넘어가도 되는데 굳이 call를 이용하나요? call을 이용하는 이유는 call를 실행하게 되면 호출된 해당 함수가 끝난 후에 다시 돌아오기 위해서 주소를 stack에 저장.. 더보기
Hello World Shellcode Hello World Shellcode 앞서서 계속 만들어본 Hello World ASM 코드를 가지고 Shellcode를 만들어 보겠습니다. HelloWorld.nasm 소스코드 입니다. 이 코드를 objdump를 이용하여 NULL이 존재하는지 확인해 보도록 하겠습니다. 역시 NULL이 존재하게 됩니다. 이제 이 소스코드를 NULL이 나오지 않게 바꿔보도록 하겠습니다. objdump 를 이용하여 확인한 결과 0x00값이 없는 것을 알 수 있습니다.이제 이 실행파일을 objdump를 이용하여 쉘 코드를 만듭니다. 쉘 코드가 만들어 졌으므로 테스트를 해봅시다. GCC로 컴파일 후 실행 시켰지만 뭔가 실행이 되긴 했습니다. 하지만 HelloWorld는 출력되지 않았습니다.이 이유는 문자열이 저장된 hello.. 더보기
Shellcoding Shellcoding 쉘 코딩이란 무엇 일까요?쉘 코드는 특별한 목적으로 사용하기 위한 기계어? 라고 하면 좋을 것 같습니다. 쉘을 실행시키는 것이 목적이죠.쉘을 실행시킨다는 것은 보안적인 관점에서 매우 중요한 부분입니다. 쉘이 실행된다는 것은 명령어를 실행 시킬 수 있다는 것이고, 명령어가 실행이 된다는 것은 어떠한 작업도 가능하다는 이야기 입니다. 쉘 코드를 만들 때 유의사항쉘 코드는 크기가 중요한데, 작은 크기일 수록 좋습니다.또한 0x00(NULL)이 들어가지 않아야 합니다. NULL 값이 들어가면 실행이 되지 않습니다. Exit Shellcode 간단하게 Exit가 실행되는 쉘 코드를 만들어 보겠습니다. exit()가 실행되는 ASM코드 입니다. 쉘 코드를 만들때 NULL이 들어가면 안된다고 했.. 더보기
Libc, Nasm Libc, Nasm 앞서 ASM 소스코드를 보면 레지스터에 값을 넣고 Syscall Number Table을 확인하여 작성했습니다. 상당히 저급언어에 가깝습니다.조금 더 프로그래머의 입장에서 편하게 사용할 수 있는 방법이 Libc를 사용하는 방법입니다. Libc Libc는 C Library를 말합니다. Libc를 이용하면 조금 더 쉽게? 사실상 쉬운것도 아니지만 조금 더 편하게 작성할 수 있습니다.문서: https://www.gnu.org/software/libc/manual/html_node/index.html Libc는 모든 인자 값이 스택에 거꾸로 들어가게 됩니다.예를 들어, call function(a, b, c, d) 라는 함수 호출을 할 때, push d, push c, push, b, pus.. 더보기
Prologue, Epilogue Prologue, Epilogue 프롤로그과 에필로그는 함수의 시작과 끝을 말합니다. Prologue 프롤로그의 ASM 코드는 다음과 같습니다.push ebpmov ebp, esp 해당 함수가 시작된 다는 것을 알려줍니다. 해당 함수에서 사용되는 크기또한 프롤로그가 시작되고 해당 공간을 확보하게 됩니다. Epilogue 에필로그의 ASM 코드는 다음과 같습니다.mov esp, ebppop ebp leaveret 에필로그는 위와 같이 2개가 있습니다. 두 개다 같은 말이라고 생각하시면 됩니다. 프롤로그에서 push와 mov를 했으니 반대로 에필로그에서는 mov와 pop를 합니다. C 컴파일을 이용하여 만든 프로그램들을 보면 프롤로그와 에필로그가 위와 같은 ASM 코드로 되어있는 것을 확인 할 수 있습니다. 더보기
Stack Stack 스택은 메모리에 값이 쌓이는 공간을 말합니다. 메모리에 어떻게 쌓이는 지 알아보도록 하죠. 먼저 스택을 알아보기 전에 Endian에 대해서 알아보도록 하겠습니다.Endian: http://kblab.tistory.com/58 자, 이제 스택구조에 대해서 알아보도록 하겠습니다. 스택은 LIFO 구조를 갖습니다. LIFO는 Last in First Out 이라는 말인데요. 나중에 들어온 것이 처음으로 나가는걸 뜻합니다. push 명령어를 통해서 스택에 쌓이게 되며 pop 명령어를 통해서 제거됩니다. 간단히 ASM코드를 작성하여 스택에 대해 좀 더 알아보겠습니다. 각 레지스터에 해당 값을 저장하고 스택에 쌓고 빼는 소스코드 입니다. 이 소스코드를 컴파일하여 실행 파일로 만들고 GDB를 이용하여 분석.. 더보기
GDB 사용법 GDB 사용법 윈도우에서는 OllyDBG, ImmunityDBG, IDA, WINDBG 등의 다양한 디버거가 존재하며, 리눅스에서는 GDB가 존재하죠. 기본적으로 GDB를 많이 사용합니다. 기본적으로 GDB를 사용하는 방법에 대해서 알아보도록 하겠습니다. 앞서 포스팅했던 HelloWorld.nasm 을 이용하여 분석하도록 하겠습니다. 분석할 때는 소스코드를 컴파일한 실행파일을 가지고 분석합니다. GDB는 다음과 같이 사용합니다. GDB 실행 gdb 실행파일 을 입력하면 위 사진과 같이 gdb가 실행이 됩니다.추가적으로 위 사진처럼 쓰잘때기 없는 글들이 나오는 것을 방지하려면 -q 옵션을 주면 글이 없이 실행됩니다. 존재하는 함수, 심볼 확인하기 해당 프로그램에 어떤 함수, 심볼이 존재하는지 확인하기 위해.. 더보기