1. 반복문


오늘은 반복문에 대해 알아보도록 하겠습니다.

반복문이란 말 그대로 반복할 때 사용하는 녀석입니다.

Go 언어에서는 반복문에 대해 for문 밖에 없습니다.

이 for문이 while문처럼 사용할 수도 있습니다.



1-1. for 문

 

위에서 말했듯이 Go 언어에서 반복문은 for문 밖에 존재하지 않습니다.

for문은 초깃값; 조건식; 증감식 { } 으로 기본 구성이 되어 있습니다.


이 for문은 c, c++, c# 등의 for 문과 동일합니다.

※ 하지만 초깃값; 조건식; 증감식 을 ( ) 로 담지 않습니다.


 

  • 초깃값은 for문이 시작할 때 기준이 되는 값이며 단 한번만 실행되는 부분입니다.
  • 조건식은 조건이 true일 경우에 코드를 실행합니다.
  • 증감식은 코드가 실행한 뒤에 이 식이 실행됩니다.


for 문 실행되는 흐름 


초깃값 → 조건식 → 코드 실행 → 증감식 → 조건식 → 코드 실행 → 증감식 ...


이제 for문의 기본 구성을 보도록 하겠습니다.



이제 간단하게 for문을 이용하여 구구단의 2단을 출력하는 코드를 작성해보겠습니다.



for 문은 여기서 마치도록 하고...


여기서 잠깐!



저도 이 강좌를 작성하기 전에 while문이 있는지 없는지 확인을 해보았습니다.

하지만 Go언어에서는 반복문은 for문만 존재하였습니다.


다만 방법이 아예 없는 것은 아녔습니다.


조건식만 설정만 해준다면 while문과 똑같이 동작하게 할 수 있습니다.


아래는 c, c++, C# 의 while문의 코드입니다.




넵, 이러한 코드를 Go언에서 작성해보겠습니다.




 

1-2. continue 문


반복문에서 코드를 실행하지 않고 넘어가게 할 때 사용합니다.


바로 코드를 보여드리겠습니다.




위에 작성된 코드를 보면 for문 안에 if문이 있습니다.

i가 2랑 나눠지며 나머지가 1일 경우에 continue 가 실행되어 코드를 실행하지 않고 넘어가게 됩니다.


그렇다면 i가 홀수일 경우에 코드를 실행하지 않는 다는 소리가 됩니다.


그렇기 때문에 결과에서 짝수만 출력되게 됩니다.


이번 강좌는 좀 짧은 것 같네요 ㅎㅎ;

아마 Go 강좌를 쓰면서 최고로 빨리 작성된 강좌가 아닌가 싶네요...


한번 작성하면 2~4시간 씩 잡아먹더라구요...ㅂㄷ

아...사실 딴짓도 하면서 목차 짜고 하면 시간이 금방 가더라구요!


다음 강좌에서는 배열에 대해 알아보도록 하겠습니다.








1. 연산자


Go 언어는 각종 기능과 라이브러리를 패키지로 만들어서 제공합니다.

이 역시 파이썬과 비슷합니다.

파이썬을 사용해보신 분들이라면 쉽게 사용하실 부분입니다.


패키지를 사용하려면 다음과 같이 import 키워드를 사용합니다.



import 키워드는 Go 강좌 2편. 안녕? Stop? GO! 편에서 소개한 적이 있습니다.


import 키워드로 가져온 패키지를 사용하는 방법은 간단합니다.

바로 패키지 이름에 점(.)을 붙여서 패키지에서 제공하는 함수, 변수, 상수를 사용합니다.



1-1. 손 쉽게 사용하기


Go 언어를 구경하다 보면 정말 마음에 드는 문법들이 너무 많아요.

패키지를 가져올 때 

import "패키지 이름" 으로 가져온다고 위에서 알려드렸습니다.

하지만 규모가 큰 프로젝트에서는 여러 패키지를 많이 사용하는 상황이 있을 수도 있습니다.

그럴 때 편하게 여러 패키지를 가져오는 방법이 있습니다.

Go lang 만세


여러 패키지를 사용하는 방법은 아래와 같이 사용합니다.



import 키워드 뒤에 괄호로 묶은 뒤에 패키지 이름을 적으며 사용하면 됩니다.



1-2. 아니 꼭 패키지.함수 이렇게 써야되나욧?




물론 대답은 No 입니다.


패키지를 전역 패키지로 가져오면 굳이 점(.)을 사용할 필요가 없어집니다.


바로 아래와 같이 사용이 가능합니다.



바로 패키지 뒤에 점(.)을 적고 import 키워드를 사용하면 전역 패키지로 가져오게 되어

패키지 이름을 적고 함수를 적을 필요가 없어지게 되는 것 입니다.


하지만 장점도 있지만 단점도 있습니다.


여러 패키지를 가져오며 함수의 이름이 중복인 패키지가 있을 수도 있습니다.


따라서 '이 방법은 좋은 방법은 아니다'라고 생각합니다.



1-3. 패키지의 이름을 Tomori Nao로 할 수 있나요?


물론 가능합니다!


패키지를 가져올 때 패키지 이름을 지정 할 수도 있습니다.


아래와 같이 패키지 이름을 지정할 수 있습니다.



위와 같이 사용하면 패키지 이름을 지정할 수 있습니다.

패키지 이름 지정은 Go 언어에서 제공하는 내가 만든 패키지와 기본 패키지의 이름이 중복이 될 때,

다른 사람이 배포하는 패키지 이름과 자신의 패키지 이름이 중복이 될 때 사용합니다.


Go 언어에서는 패키지를 가져온 뒤 사용을 안하면 컴파일 에러가 발생합니다.

따라서 사용하지 않는 패키지 이름 앞에 언더 바(_)를 지정하면 컴파일 에러가 발생하지 않습니다.


패키지 이름을 빈 식별자로 사용하여 컴파일 에러를 방지 할 수 있습니다.

실제로 사용하는 사례는 스택오버플로우에 답변에서 찾아볼 수 있었습니다.



저와 같은 생각을 하는 질문자가 있었습니다.

과연 프로젝트에서 이러한 코드를 사용할까?

실제 사례가 있나요 라고 질문하는 질문자.



넵 사용은 하는 것 같네요.



2. 조건문(if, else, switch)


조건문이란 말 그대로 조건의 결과에 따라 실행되는 것이 결정됩니다.

프로그래머의 프로그램의 흐름을 원하는 형태로 컨트롤을 할 수 있게 해주는 녀석이죠.


아래 표에 정리된 조건문들에 대해서 배워보도록 하겠습니다.



 조건문 종류

 설명

 if

 말 그대로 if문은 만약에~ 할 경우 

if 오늘 금요일 {

불타오른다()

}

 else

 else 문은 아닐 경우~

if 오늘 금요일 {

불타오른다()

} else {

시체가된다()

}

 elseif

 이것도 아닐 경우 만약에 ~ 의 경우

if 오늘 금요일 {

불타오른다()

} else if  오늘 토요일 {

토요일이니까불타오른다()

} else {

시체가된다()

 switch

 switch 문은 맞을 경우? (?)

switch 요일 {

case 월:

시체가된다()

case 화:

시체가된다() 

case 수:

생기가띈다()

....

case 금:

불타오른다() 

case 토:

불타오른다() 

case 일:

자살하고싶다()

default:

이건무슨요일일까요()

// 모든 Case에 해당하지 않을 때 실행됩니다.

}



(1) if문


if문이란 조건문의 기본 중의 기본인 녀석입니다.

프로그래머가 특정 조건을 설정하여 프로그램의 흐름을 제어할 때 사용합니다.


if문의 기본 구성은 이러합니다.



관계 연산자와 if 문을 이용하여 간단한 코드를 작성해보겠습니다.


이것을 쉽게 그림으로 표현하면 이렇게 됩니다.



위 순서도를 보았을 때,

10이 5보다 큰가의 조건으로 주어지면서 클 경우에는 "이야~ 10이 더 크구먼유~"를 출력하며

아닐 경우에는 해당하는 조건이 없기 때문에 아무것도 실행하지 않습니다.



(2) else문


else 문은 if 문의 부족함을 채워주는 녀석이라고도 과언이 아닙니다.

위에서의 if 문에서의 조건을 보면 부족한 부분이 있다고 생각을 할 수 있습니다.


아니..10이 5보다 클 경우에의 조건을 넣고 싶은데!

그럴 때의 사용하는게 else 문입니다.


어떤 조건에 해당하지 않는 경우에 대해서 처리를 할 수 있도록 하는 녀석이 else문 입니다.

우선 if ~ else문의 기본 구조를 보도록 하겠습니다.


이 역시 관계 연산자와 if ~ else 문을 이용하여 간단한 코드를 작성해보았습니다.




이것을 쉽게 그림으로 표현하면 이렇게 됩니다.



위 순서도를 보았을 때,

10이 5보다 큰 가의 조건으로 주어지면서 클 경우에는 "이야~ 10이 더 크구먼유~"를 출력하며

아닐 경우에는 "5가 작네유..." 를 출력합니다.



(3) elseif문


이 elseif문은 if~else 문의 부족함을 완벽히 채워주는 그런 녀석입니다.

더 많은 조건을 추가해서 마음대로 흐름을 제어할 수 있게 되는 것이죠.

하나의 조건이 거짓일 때 elseif문의 조건식을 검사합니다.


일단 else if 문의 기본 구조를 보도록 하겠습니다.



이 역시 관계 연산자와 else if 문을 이용하여 간단한 코드를 작성해보았습니다.




이것을 쉽게 그림으로 표현하면 이렇게 됩니다.



넵...확실히 순서도가 이제 좀 커졌다 라고 생각이 들 정도네요.


위 순서도를 보았을 때,

10이 5보다 큰 가의 조건으로 주어지면서 클 경우에는 "이야~ 10이 더 크구먼유~"를 출력하며

아닐 경우에는 새로운 조건이 5와 5는 같은가? 라는 조건이 주어집니다.

조건이 맞을 경우에는 "같당께유!"를 출력하며 아닐 경우에는 "5가 작네유..." 를 출력합니다.



(4) switch문


이제 switch문에 대해 알아보도록 하겠습니다.


switch문은 if문과 달리 여러개의 조건식의 결과를 검사할 때 사용됩니다.


switch문의 기본 구조를 보도록 하겠습니다.



각 Case를 비교를 하여 값이 일치하면 해당 Case에 있는 코드를 실행하고 switch문을 중단합니다.

그리고 모든 Case에 해당하지 않을 때에 default를 실행합니다.

여기서 Go언어의 특이한 점은 Switch 문에 break 키워드를 생략한 다는 것 입니다. 


switch문을 사용하는 예제 코드를 작성해보았습니다.




그냥 쉽게 switch문을 이해하자면 if 문이 여러개 박혀있다고 생각하시면 이해가 빨리 될 것 같네요.


switch에 day 의 값을 비교하게 한 뒤에 각 맞는 case를 실행하게 됩니다.

day 에는 금이 들어있으므로 case 금이 실행됩니다.

※ case에는 문자열 뿐만 아니라 숫자도 가능합니다!


(5) Switch in fallthrough, break


이 위의 내용에서 다 작성한 줄 알았더니 구글링을 통해 Go 언어는 break를 어떻게 사용하나 알아보려 했는데.

새로운 키워드를 알게 되었습니다.

그래서 둘 다 정리할 겸 작성해봅니다.



(5-1) fallthrough


사실 이 녀석을 언제 써먹을 지 생각이 나질 않네요...

딱히 쓸 일은 없을 것 같은 녀석입니다.

특정 case의 코드를 실행한 뒤 다음 case를 실행하고 싶을 때

fallthrough 키워드를 사용합니다.


switch문에서 fallthrough를 사용하는 예제 코드를 작성해보았습니다.




다만 fallthrought 키워드는 맨 마지막 case에서는 사용할 수 없습니다.

ps. 그냥 fallthrought 키워드는 switch에서 break 빼서 다음 case 실행되는 것과 같은 것 같아요.



(5-2) break


switch에서 break를 사용할 때에는 if문 안에 쓰는 것 같아 보입니다.



위키에서도 if문을 빠져나올 때 사용한 걸로 보아...

쿰척..쿰척...




위와 같이 됩니다.


만약 b1이 cat이 아닐 경우에는 0만 출력하게 되는 것이죠.



이번 강좌에서는 조건문과 패키지에 대한 설명이 끝났습니다.

긴 글을 읽어주셔서 감사합니다.


다음 강좌에서는 반복문에 대해 알아보도록 하겠습니다.






Merry Chritmas !


1. 연산자


이번 강좌에서는 Go 언어에서 사용하는 연산자에 대해 알아보겠습니다.

연산자에는 여러 종류의 연산자가 있습니다.

모두 다 외울 필요는 없습니다!

아래 표에 정리된 연산자들에 대해서 배워보도록 하겠습니다.


 분류

 연산자

 수식 연산자

 +, -, *, /, %

 증감 연산자

 ++, --

 할당 연산자

 =, :=, +=, -=, *=, /=, %=, &=, |=, ^=, &^=, =<<, =>>

 논리 연산자

 &&, ||, !

 관계 연산자

 ==, !=, <, <=, >, >=

 비트 연산자

 &, |, ^, &^, <<, >>, ^

 채널 연산자

 <-

 메모리(?)

 &, *



(1) 수식 연산자


수식 연산자에는 덧셈, 뺄셈, 나눗셈, 그리고 나눗셈을 한 뒤의 나머지 구하는 연산자들이 있습니다.

아래에는 그 연산자들을 표로 정리한 것입니다.



 분류

 기능

 설명

 +

 덧셈

 두 값을 더합니다.

※ 값의 자료형은 정수, 실수, 복소수, 문자열입니다.

 -

 뺄셈

 두 값을 뺍니다.

※ 값의 자료형은 정수, 실수, 복소수입니다.

 *

 곱셈

 두 값을 곱합니다.

※ 값의 자료형은 정수, 실수, 복소수입니다.

 /

 나눗셈

 두 값을 나눕니다.

※ 값의 자료형은 정수, 실수, 복소수입니다.

 %

 나머지

 두 값을 나눈 뒤 나머지를 구합니다.

※ 값의 자료형은 정수입니다.


아래에 있는 코드는 수식 연산자를 사용하여 그 결과를 출력하는 예제입니다.



(2) 증감 연산자


증감 연산자에서는 값을 1만큼 증가시키는 증가 연산자와 1만큼 감소시키는 감소 연산자가 있습니다.
아래에는 그 연산자들을 표로 정리한 것입니다.

 분류

 기능

 설명

 ++

 값을 1만큼 증가

 값을 1만큼 증가합니다.

Go 언어에서는 ++연산자를 사용한 뒤 값을 대입할 수 없습니다.

변수 뒤에서만 사용할 수 있습니다.

ex) count++ (O), bount := count++ (X)

※ 값의 자료형은 정수, 실수, 복소수입니다.

 --

 값을 1만큼 감소

 값을 1만큼 감소합니다.

Go 언어에서는 --연산자를 사용한 뒤 값을 대입할 수 없습니다.

변수 뒤에서만 사용할 수 있습니다.

ex) count-- (O), bount := count-- (X)

※ 값의 자료형은 정수, 실수, 복소수입니다.


아래에 있는 코드는 증감 연산자를 사용하여 그 결과를 출력하는 예제입니다.




(3) 할당 연산자


할당 연산자에서는 말 그대로 연산을 한 뒤에 그 값을 할당합니다.

아래에는 그 연산자들을 표로 정리한 것입니다.



 분류

 기능

 설명

 =

 대입

 변수나 상수에 값을 대입합니다.

※ 변수는 변수끼리 대입을 할 수 있습니다.

 :=

 변수 선언 및 대입

 변수 선언 및 대입

변수를 선언하는 동시에 값을 대입할 수 있습니다.

 +=

 덧셈 후 대입

 현재 변수와 값을 더한 후에 다시 변수에 대입합니다.

※ 문자열일 경우 현재 변수에 문자열을 붙인 다음 다시 변수에 대입합니다.

 -=

 뺄셈 후 대입

 현재 변수에서 값을 뺀 다음 다시 변수에 대입합니다.

 *=

 곱셈 후 대입

 현재 변수에서 값을 곱한 다음 다시 변수에 대입합니다.

 /=

 나눗셈 후 대입

 현재 변수에서 값을 나눈 다음 다시 변수에 대입합니다.

 %=

 값의 나머지를 대입

 현재 변수와 값의 나머지를 변수에 대입합니다.

 &=

 AND 비트 연산 후 대입

 현재 변수를 값으로 AND 연산한 다음 다시 변수에 대입합니다.

 |=

 OR 비트 연산 후 대입

 현재 변수를 값으로 OR 연산한 다음 다시 변수에 대입합니다.

 ^= XOR 비트 연산 후 대입 현재 변수를 값으로 XOR 연산한 다음 다시 변수에 대입합니다.
 &^= AND NOT 비트 연산 후 대입 현재 변수를 값으로 AND NOT 연산한 다음 다시 변수에 대입합니다.
 <<= 비트를 왼쪽으로 이동 후 대입 현재 변수를 값 만큼 왼쪽으로 이동한 다음 다시 변수에 대입합니다.

 >>=

 비트를 오른쪽으로 이동 후 대입 현재 변수를 값 횟수만큼 오른쪽으로 이동한 다음 다시 변수에 대입합니다. 


아래에 있는 코드는 할당 연산자를 사용하여 그 결과를 출력하는 예제입니다.




bit





(4) 논리 연산자


논리 연산자는 참, 거짓 두가지 원소만 존재하는 집함에서의 연산입니다.

흔히 비트 연산이라고도 합니다.


밑에 있는 표는 연산자들의 진리표입니다.


 A

 B

 OUT

 0

 0

 0

 0

 1

 0

 1

 0

 0

 1

 1

 1

[ && 연산자의 진리표 ]


 A

 B

 OUT

 0

 0

 0

 0

 1

 1

 1

 0

 1

 1

 1

 1

[ || 연산자의 진리표 ]


 A

 OUT

 0

 1

 1

 0

[ ! 연산자의 진리표 ]


아래에 있는 코드는 논리 연산자를 사용하여 그 결과를 출력하는 예제입니다.




(5) 관계 연산자


관계 연산자는 두 값의 관계를 알아보는 연산자입니다.

크기를 비교하거나 값이 서로 같거나 다른지 확인하는 연산자들이 있습니다.

아래는 관계 연산자를 표로 정리해 둔 것입니다. 


 분류

 기능

 설명

 ==

 같음

 두 값이 같으면 참을 반환하며 다를 경우 거짓을 반환합니다.

 !=

 다름

 두 값이 다르면 참을 반환하며 다를 경우 거짓을 반환합니다.

 <

 작음

 왼쪽 값이 크면 참을 반환하며 다를 경우 거짓을 반환합니다.

 <=

 같거나 작음

 오른쪽 값과 왼쪽 값이 같거나 왼쪽 값이 클 경우 참을 반환하며 다를 경우 거짓을 반환합니다.

 >

 큼

 오른쪽 값과 왼쪽 값이 같거나 왼쪽 값이 클 경우 참을 반환하며 다를 경우 거짓을 반환합니다.

 >=

 같거나 큼

 오른쪽 값과 왼쪽 값이 같거나 왼쪽 값이 클 경우 참을 반환하며 다를 경우 거짓을 반환합니다.


아래에 있는 코드는 관계 연산자를 사용하여 그 결과를 출력하는 예제입니다.






(6) 비트 연산자


비트 연산자는  말 그대로 비트를 다루는 연산자입니다.

아래는 비트 연산자를 표로 정리해 둔 것입니다.


 분류

 기능

 설명

 &

 AND 비트 연산

 두 값을 비트 단위로 AND 연산을 합니다.

 |

 OR 비트 연산

 두 값을 비트 단위로 OR 연산을 합니다.

 ^

 XOR 비트 연산

 두 값을 비트 단위로 XOR 연산을 합니다.

※ 사용할 수 있는 값의 자료형은 정수입니다.

 &^

 AND XOR 비트 연산

 두 값을 비트 단위로 AND NOT 연산을 합니다.

※ 사용할 수 있는 값의 자료형은 정수입니다.

 <<

 비트를 왼쪽으로 이동

 현재 값의 비트를 값 만큼 왼쪽으로 이동합니다.

※ 사용할 수 있는 값의 자료형은 정수입니다.

 >>

 비트를 오른쪽으로 이동

 현재 값의 비트를 값 만큼 오른쪽으로 이동합니다.

※ 사용할 수 있는 값의 자료형은 정수입니다.


아래에 있는 코드는 비트 연산자를 사용하여 그 결과를 출력하는 예제입니다.




(7) 채널 연산자


채널 연산자는 채널을 사용할 때 이용하는 연산자입니다.

채널이랑 고루틴끼리 데이터를 주고 받고, 실행 흐름을 제어하는 기능입니다.

이 채널이라는 것은 후에 자세히 설명할 예정이오니 이러한 녀석도 있구나! 하고 보시면 되겠습니다.

아래는 채널 연산자를 표로 정리해 둔 것입니다.



 분류

 기능

 설명

 <-

 채널 수신 연산

 채널에 값을 보내거나 값을 가져옵니다.


아래에 있는 코드는  채널 연산자를 사용하는 예제입니다.






(8) 메모리(?)


이걸 연산자라고 해야될지 뭐라해야할 지 잘 모르겠습니다...

그냥 설명을 안할까 싶기도 했지만 그냥 하는 것이 좋을 것 같아 추가로 작성하겠습니다.

이 녀석은 쉽게 생각하자면 포인터같은 개념입니다.

메모리에 접근이 가능합니다.

아래는 메모리를 접근할 수 있는 아이들을 표로 정리해 둔 것입니다.


 분류

 기능

 설명

 &

 참조

 현재 변수의 메모리 주소를 구합니다.

 *

 역 참조

 현재 포인터 변수에 저장된 메모리에 접근하여 값을 가져오거나 저장합니다.


아래에 있는 코드는  위의 표에 있는 아이들을 사용하는 예제입니다.




이번 강좌에 많은 연산자들에 대한 설명이 끝났습니다.

이 긴 글을 읽어주셔서 감사합니다.


다음 강좌에서는 패키지 사용법조건문에 대해 알아보도록 하겠습니다.



1. 변수(Variable)



수학을 공부하신 분들은 '변수'에 대해 알고 계실 겁니다.


수학에서의 변수와 프로그래밍에서 쓰이는 변수는 비슷합니다.


프로그래밍의 변수는 쉽게 말하자면 무언가를 담는 박스라고 생각하시면 됩니다.


값을 저장할 수 있고, 우리가 필요할 때는 그 값을 참조하여 사용할 수 있습니다. 


잠깐의 이해를 도우려고 간단한 코드로 예를 들어보겠습니다.


시나리오

"어머니께서 심부름을 시키기 위해 천 원을 주셨습니다.

상추 한 잎을 사오라고 하셨습니다.

상추 한 잎에 800원이라고 하였을 때, 우리가 거스름돈으로 받아야 할 금액은 200원이 됩니다."


이에 상황에 맞는 코드를 작성하면 이러한 코드가 됩니다. 



위의 코드에서의 money, sangchoo 는 변수이며 변수가 담고 있는 값을 잘 출력하는 것을 볼 수 있습니다.

9행에 보시면 money 변수에 1000이라는 값이 들어가고

10행에서는 sangchoo 변수에 800이라는 값이 들어가는 것을 볼 수 있습니다.


12~13행은 그 변수 안에 있는 값을 가져와 출력을 하는 것을 볼 수 있으며,

15행에서는 각 변수의 값들을 뺀 값을 출력을 합니다.



1-1. 깊게 파고 들자


Go 언어에서 변수를 선언하는 방법은 var 키워드를 사용하는 방식과 자료형을 생략하는 방법이 있습니다.



(1) Var 키워드



※ var 변수명 데이터 타입


다른 언어와는 달리 데이터 타입이 변수 명의 뒤에 옵니다.


변수 명을 작성할 때의 주의하여야 할 점이 있습니다.

변수 명은 문자와 숫자로 이루어져야 합니다.

※ 단, 문자 또는 _ (언더 바)로 시작해야 하며 숫자로 시작할 수 없습니다.


ex) hubeen0(O), _hubeen_is_sexy(O), 18Love22(X)



(2) 자료형 생략 선언




자료형을 생략하고 선언을 하시려면 반드시 초기 값을 대입하셔야 됩니다.

자료형을 생략하고 초기 값을 대입하면 초기 값의 데이터 타입으로 결정됩니다.


ex) var a = 10(O), var b = "fukkk"(O), var sexy(X)



(3) 손 쉽게 선언하기


빠른 선언과 여러 개의 변수를 선언하는 방법을 소개합니다.



 :=를 사용하면 위에서 소개한 방법들을 사용하지 않고 간단히 변수를 선언하고 초기화 할 수 있습니다.

여기서 변수의 타입은 대입 값의 타입으로 선언됩니다.



이 변수 선언 방법은 if, for 제어문 안에서 임시 변수를 사용할 때 매우 유용하게 사용됩니다.


변수를 여러 개 선언할 때에는 콤마(',')로 구분하여 나열합니다.

여기서 나열되어 선언되는 변수의 대입되는 개수는 서로 같아야됩니다.

여기서 변수의 타입은 지정하지 않으면 변수의 타입은 대입되는 값의 타입으로 선언됩니다.



변수를 여러 개 선언할 때에도 처음에 소개한 방법으로도 선언이 가능합니다.



이런 식으로도 선언이 가능합니다.

ex) var a,b,c int = 10,20,30 (O), var a,b = 10, "Hello" (O), a,b,c := 10,20,30,40 (X)


1-2. 기본 데이터 형식 (Basic types)



Go 언어에서 제공하는 기본 데이터 형식들을 표로 정리하였습니다.

※ 잘못 번역된 부분도 있을 수 있으며 틀릴 수도 있습니다.



 구분

 데이터 형식

 설명

 값의 범위

 논리

 bool

 -

 True, False 

 문자열

 string

 -

 -

 정수

 rune

 int32와 크기가 동일, 

유니코드 문자코드를 저장할 때 사용

 -

 정수

 int

 32bit에서는 int32

64bit에서는 int64

 -

 정수

 int8

 부호 있는(signed) 8비트,

1바이트 정수

 -128 ~ 127

 정수

 int16

 부호 있는(signed) 16비트,

2바이트 정수

 -32768 ~ 32767

 정수

 int32

 부호 있는(signed) 32비트,

4바이트 정수

 -2147483648 ~ 2147483647

 정수

 int64

 부호 있는(signed) 64비트,

8바이트 정수

 -9223372036854775808 ~ 9223372036854775807

 정수

 uint

 32bit에서는 uint32

64bit에서는 uint64

 -

 정수

 uint8

 부호 없는(unsigned) 8비트,

1바이트 정수

 0 ~ 255

 정수

 uint16

 부호 없는(unsigned) 16비트,

2바이트 정수

 0 ~ 65535

 정수

 uint32

 부호 없는(unsigned) 32비트,

4바이트 정수

 0 ~ 4294967295

 정수

 uint64

 부호 없는(unsigned) 64비트,

8바이트 정수

 0 ~ 18446744073709551615

 정수

 uintptr

 uint와 크기가 동일하며,

포인트를 저장할 때 사용

 -

 정수

 byte

 uint8와 크기가 동일하며,

바이트 단위로 저장할 때 사용

 -

 실수

 float32

 IEEE-754 

32비트 단정밀도 부동 소수점,

7자리 정밀도 보장

 -

 실수

 float64

 IEEE-754 

64비트 배정밀도 부동 소수점,

15자리 정밀도 보장

 -

 실수

 complex64

 float32 크기의 실수부와 허수부로 된 복소수

 -

 실수

 complex128

 float64 크기의 실수부와 허수부로 된 복소수

 -



(1) 정수 데이터 형식


정수 데이터 형식의 변수에 값을 담고 그 값을 출력하는 예제를 작성할 수 있습니다.





(2) 실수 데이터 형식


실수 데이터 형식의 변수에 값을 담고 그 값을 출력하는 예제를 작성할 수 있습니다.





(3) 문자열 데이터 형식


문자열 데이터 형식의 변수에 값을 담고 그 값을 출력하는 예제를 작성할 수 있습니다.





(4) 논리 데이터 형식


논리 데이터 형식의 변수에 값을 담고 그 값을 출력하는 예제를 작성할 수 있습니다.





2. 형식 변환(Type conversion)



형식 변환이란 다른 데이터 형식에 옮겨 담기 위해 데이터 형식을 변환하는 것을 말합니다.

쉽게 말하자면 이 변수는 정수를 받는 박스인데 

저희가 집어넣으려는 값을 실수인 상황인 겁니다!

이러한 상황에서는 실수를 정수로 변환한 뒤에 넣어야 되게 되기 때문에

이러한 상황에 쓰이는 녀석입니다.


아래에 예제들을 보도록 하겠습니다.



2-1. 정수 → 실수, 실수 → 정수 형 변환

정수에서 실수로 변환하는 과정과 실수에서 정수로 변환하는 과정의 차이를 눈으로 확인할 수 있습니다.



정수에서 실수로 바꾸는 과정에서는 아무 문제 없이 잘 바뀜을 알 수 있습니다.



실수에서 정수로 바꾸는 과정에서는 소수점 아래는 모두 버리는 것을 알 수 있습니다.

500.1234에서 소수점 아래의 1234가 버려지고 b에 들어가는 것을 알 수 있습니다.



3. 상수(Constants)



상수란 무엇일까요?


상수(常數)란 수식에서 변하지 않는 값을 뜻한다. 이것은 변하는 값 변수와 반대이다.

라고 위키백과에 적혀있습니다.


위와 같이 상수란 변하지 않는 고정된 값입니다.


수천 줄 수만 줄인 프로그램을 작성하게 될 때 수 많은 변수를 선언하게 됩니다.

그러나 이 수많은 변수들 중 값이 도중에 변경되어도 프로그램에 영향을 주는 변수와 도중에 변경되면 심각하게 영향을 주는 변수가 있습니다.

ex) admin, auth


만약에 코드를 작성하다가 나도 모르게 값을 변경하지 말아야 되는 변수를 건드려서 버그가 생길 수 있다는 겁니다!


Go 언어에서는 const 키워드로 상수를 만들 수 있습니다.


이 const 키워드를 선언과 동시에 초기화된 값은 절대로 변하지 않습니다. 



위와 같이 선언할 수 있습니다.


변수와 마찬가지로 문자 또는 언더바 ('_')로 시작하여야 하며 숫자로는 시작할 수 없습니다.

ex) 18Love22, 7eleven, 24hour


그리고 이 아래의 있는 코드는 잘 못된 코드입니다.




(1) 자료형 생략 선언



변수와 같이 자료형을 생략하고 선언이 가능합니다.



위와 같이 선언할 수 있습니다.

변수처럼 초기에 대입하는 값의 자료형으로 선언됩니다.


※ 초깃 값을 꼭 대입해줘야 됩니다. 



(2) 여러 개 선언



상수를 여러 개 선언하고 초기화할 때는 변수와 값을 콤마(',')로 구분하여 나열합니다.

상수를 선언한 순서대로 값이 대입되며 반드시 선언한 상수의 개수와 대입할 값의 개수가 같아야 합니다.


선언할 때 자료형을 지정하지 않으면 대입하는 값의 자료형으로 선언됩니다.



또 const 키워드와 ( )를 사용하면 상수 여러 개를 한 번에 선언하고 초기화할 수 있습니다.



여기서도 상수의 자료형을 지정하거나 생략할 수 있습니다.

마찬가지로 자료형을 지정하지 않으면 상수의 자료형은 대입하는 값의 자료형으로 결정됩니다.


※ 변수와는 달리 초깃값을 반드시 지정해줘야 됩니다.


이번 강좌에 변수와 데이터 형식 그리고 상수에 대한 설명이 끝났습니다.

이 긴 글을 읽어주셔서 감사합니다.


다음 강좌에서는 연산자(Operators)에 대해 알아보도록 하겠습니다.








1. 통합 개발 환경 사용하기


Go 언어는 딱히 통합 개발 환경(Integrated Development Environment, IDE)이 없어도 텍스트 편집기와 컴파일러만으로도 개발이 가능합니다.

하지만  통합 개발 환경(Integrated Development Environment, IDE)을 사용하면 좀 더 편하게 개발이 가능합니다.


이 강의에서는 LiteIDE를 이용 할 겁니다.

이 IDE는 여러 GO 언어 IDE에서 가볍기 때문에 이 녀석으로 선택하였습니다.

 

이 IDE에 대해 소개하자면,

꾸준히 지속적으로 업데이트를 하고 있으며 리눅스, MAC OS X, Windows를 모두 지원합니다.

설치 방법은 LiteIDE 홈페이지에서 압축 파일을 받은 뒤에 압축 해제를 하여 실행 파일을 실행하면 됩니다.


◎ 공식 웹 사이트 주소 : https://code.google.com/p/golangide/

◎ 다운로드 및 설치 : https://code.google.com/p/golangide/wiki/liteidex



1-1. LiteIDE 설치&실행하기


다운로드 페이지에 정상적으로 접속하였다면 아래와 같은 페이지를 보실 수 있습니다.

자신의 OS에 맞게 설치를 하시면 됩니다.



받은 파일을 압축을 잘 풀으시고 



이 녀석이 저희가 사용할 LiteIDE 입니다.

이 녀석을 실행하시면 아래와 같이 LiteIDE가 실행됩니다.




1-2. 프로젝트 생성과 Helloworld 출력하기



일단 실행을 하셨다면 프로젝트를 생성을 해보도록 하겠습니다.


File - New(Ctrl+N) - Project 또는 File 선택



OK 버튼을 통해 생성을 해줍니다.



잠깐 여기서 단축키를 소개하겠습니다.


  • Ctrl + B : 빌드
  • Ctrl + R : 빌드 및 실행(디버깅 하지 않음)
  • Ctrl + T : 테스트(?)
  • F9 : BP 설정 및 해제
  • F5 : 디버깅 시작 및 계속(Continue)
  • Shift + F5 : 디버깅 중지



넵, Ctrl + R 로 빌드 및 실행을 하였습니다.

아래의 Build Output에 Hello World! 를 띄우면서 

왼쪽에 있는 폴더에 Helloworld.exe 가 빌드가 된 것을 볼 수 있습니다.


Go 언어는 모든 부분이 패키지로 구성되어 있습니다.

따라서 다음과 같이 소스 코드의 맨 위에 패키지 설정이 들어갑니다.

Go 언어로 작성한 프로그램은 main 패키지부터 실행됩니다.



import는 패키지를 사용하는 키워드입니다.

여기서 문자열을 출력하기 위해 fmt 패키지를 사용합니다



Go 언어는 모든 언어와 마찬가지main 함수부터 시작합니다.

그리고 fmt 패키지Println 함수를 이용하여 Hello World! 문자열을 출력합니다.




1-3. 에러 해결 방법



처음 빌드를 할 때나 디버깅을 할 때 에러가 나올 수가 있습니다.


Error: process failed to start.

의 에러가 나타난다면.


View - Edit Environment 



GOROOT= 의 경로가 자신이 Go 를 설치한 경로가 맞는 지 확인하셔야 됩니다.


자신이 설치한 경로가 맞게 설정된 후에 다시 빌드를 하시면 성공적으로 되는 것을 보실 수 있습니다.



2. Go언어 문법 알아보기.



Go 언어는 파이썬과 같이 일부 문법의 작성 스타일을 강제로 하고 있습니다.

대표적으로 들여쓰기와 중괄호 표기 방법입니다.


하지만 이러한 작성 스타일을 강제로 하게 된다면 여러 사람과 협업을 할 때

코드가 일관성 있게 보이기 때문에 가독성이 높아지는 장점이 있습니다.


다른 언어들은 통일된 작성 규칙이 없기 때문에 같은 언어라도 작성하는 사람에 따라 전혀 다른 스타일의 코드가 나오게 됩니다.

바로 아래와 같은 현상이 일어납니다... (OMG...)



무려 이러한 스타일들로 몇몇 프로그래머 분들은 나의 코딩 스타일이 가독성이 끝내준다구!

하면서 다툼도 일어나기도 합니다... (거짓말이 아닌 진짜입니다... 이런! 전투민족들!)


따라서 스타일이 다르면 가독성이 떨어져 코드를 읽고 쉽게 이해하기가 힘이 들게 됩니다.


파이썬은 들여쓰기를 문법으로 강제로 하기 때문에 각 개발자의 취향이 덜 드러나고, 코드가 일관성이 있습니다.

Go 언어도 파이썬처럼 문법 스타일을 강제로 합니다.

그리고 컴파일러와는 별도로 문법 스타일을 자동으로 맞춰주는 편리한 녀석이 있습니다.


바로 gofmt 명령입니다.

이 명령은 소스 파일의 내용을 정렬하여 표준 출력으로 보여줍니다.

중괄호, 들여쓰기를 자동으로 표준에 맞게 출력을 해주죠!

매우 편리한 녀석입니다.



2-1. 깊게 알아 보자



Go 언어는 문법이 조금 독특합니다.

C언어 기반 언어라 C언어와 조금 비슷하여 C언어 냄새를 내뿜지만, Go 언어만의 냄새도 풍기고 있습니다.


첫 번째로 세미클론(';')입니다.


여러 언어에서는 구문이 끝날 때 뒤에 세미클론(';')을 붙여야만 합니다.

하지만 Go 언어에서는 보통 구문 마지막의 세미클론(';')을 생략합니다.



다음과 같이 한 줄에 여러 구문을 사용할 경우에만 세미클론(';')으로 구분하면 됩니다.



보통 이러한 코드 스타일을 사용하질 않으면 gofmt 명령을 사용하면 자동으로 여러 줄로 바꿔줍니다.

LiteIDE 에서 빌드를 하면 자동으로 여러 줄로 바꿔집니다


두 번째는 중괄호입니다.


Go 언어는 중괄호는 아래와 같이 구문의 바로 뒤에 위치합니다.



아래와 같이 하면 틀린 문법이 되는 겁니다.

Go 언어는 컴파일 할 때 줄의 마지막에 세미클론(';')을 자동으로 붙입니다.

따라서 중괄호를 새 줄부터 시작하면 앞 줄의 마지막에 세미클론(';')이 붙게 되서 에러가 납니다.


if 1==1; 이런 식으로 돼서 에러가 나게 된다는 겁니다.



넵...이렇게 노하십니다.

빨간 글씨로 말이죠.


세 번째는 들여쓰기입니다.


Go 언어는 들여쓰기에 탭(TAB)을 사용합니다.

스페이스 바로 들여쓰기를 했더라도 gofmt 명령으로 소스를 정렬하면 모두 탭으로 변경됩니다.

gofmt 만세!


네 번째로는 주석입니다.


코드에 소설을 작성하시고 싶으실 때나 코드의 역할에 대해 잊어먹지 않으려고 간단히 작성할 때 사용하는 주석.


주석은 다른 언어들과 큰 차이가 없습니다.

아래의 이미지처럼 시를 작성할 수도 있고 코드의 역활에 대해 잊어먹지 않으려고 간단히 쓸 수도 있습니다.



주석에는 한 줄 주석과 범위 주석 두 가지가 있습니다.


이 녀석들이 한 줄 주석이며,



이 녀석들이 범위 주석입니다.



범위 주석은 한줄에서 일부분만 주석으로 만들 수 있으며 여러 줄을 주석으로 만들 수 있습니다.



2-2. 수식 계산



이번에는 간단한 수식 계산식을 통해 결과를 확인해보도록 하겠습니다.

우선 이 부분에 대해서는 초등학교 수학을 배우셨다면 쉽게 이해할 수 있는 부분입니다.


먼저 하기에 앞서 Go 언어의 연산자 우선 순위 표를 보도록 하겠습니다.


 우선 순위

 연산자

 5

 * / % << >> & &^

 4

 + - | ^|

 3

 == != < <= > >=

 2

 &&

 1

 ||


연산자 우선순위는 수학시간에 배운 내용과 비슷합니다.
식에 곱샘과 덧샘이 있다면 곱샘부터 계산한 뒤에 덧샘을 계산합니다.

Go 언어에서는 연산자들이 있을 때 왼쪽에서 오른쪽으로 계산을 하며, 연산자들은 위의 표의 우선순위가 있으며 숫자가 높을 수록 먼저 계산합니다.



넵, 코드에서 위에서부터 더하기, 빼기, 나누기, 곱하기, 나머지입니다.


아래의 표는 연산자들 입니다.


 연산자

 예시

 설명

 +

 a + b 

 a 와 b를 서로 더합니다.

 -

 a - b

 a 와 b를 서로 뺍니다.

 *

 a * b

 a 와 b를 서로 곱합니다.

 /

 a / b

 a 와 b를 서로 나눕니다.

 %

 a % b

 a 와 b를 나눈 나머지 값을 가져옵니다.


다음 편에서는 Go 언어의 변수에 대해 알아보도록 하겠습니다.





1. 시작


시작하기에 앞서 제 이야기를 끄적여보겠습니다!

이번에 작성하는 강좌는 제발 꾸준히 끝까지 썼으면 좋겠네요...

끈기가 없어서 9강 정도까지 작성하면 그 뒤로는 귀찮아서 작성을 하지 못했었는데...


이번 Go 언어를 공부하게 된 계기는 서점에서 2시간을 버텨야 되는 상황에...

제 눈에 들어온 Go 언어 책을 발견하였습니다.


심심하여 보았는데...


꽤 매력적인 언어라고 생각해서 이렇게 공부를 시작하게 되었습니다.


저도 공부를 하며 작성하는 강좌이기 때문에 다른 사람들이 최소한 쉽게 알아들을 수 있게 작성할 예정입니다.

저도 이제 막 공부를 하고 있어 다른 사람들이 이해할 수 있게 자세히 작성하는 데에는 무리가 있겠지만,

최대한 다른 사람들이 이해할 수 있게 작성하는 것을 최우선적인 목표로 작성할 겁니다.


필자가 누구를 가르칠 수 있는 정도는 아니지만 공부해왔던 지식을 공유를 한다는 개념으로 작성하는 강좌입니다.


서론이 길어졌네요.


우선 Go 언어에 대해 설명을 하여 알아보도록 합시다.


Go 언어는 구글이 개발한 프로그래밍 언어입니다.

2007년에  켄 톰슨, 롭 파이크, 로버트 그리즈머가 최초 설계를 시작했으며 2008년부터 본격적으로 개발을 하였습니다.

특히 C언어의 모체가된 B언어와 유닉스의 개발자로 유명한 켄 톰슨이 참여하여 큰 화제가 된 언어입니다.


Go 언어는 빠른 성능, 안정성, 편의성, 쉬운 프로그래밍을 목표로 개발되었으며 범용 프로그래밍 언어입니다.


Go 언어의 문법은 대체로 C와 비슷합니다.

C와는 다르게 세미클론이 필수가 아닌 옵션입니다.


간결한 문법을 추구하는 Go 언어...!


이제 설치를 해보도록 하겠습니다.


2. 설치


Go 언어 설치하기 위해 우선은 Go 언어 홈페이지의 다운로드 페이지로 이동해야 됩니다.

파이썬 홈페이지는 Here!를 누르면 이동합니다.

정상적으로 이동을 하셨다면 아래와 같은 다운로드 페이지를 보실 수 있습니다.

자신의 OS에 맞게 설치를 하시면 됩니다.



2-1. Linux (Ubuntu, CentOS) 


리눅스에서 설치하는 방법은 여러가지입니다.

먼저 Go 언어 공식 웹사이트에 올라와 있는 바이너리를 받아서 설치하는 방법입니다.


우선 wget 명령으로 tar.gz 파일을 받습니다.



성공적으로 받아졌습니다.



압축을 풀어줍니다.


그리고 Go 명령을 사용할 수 있도록 .bashrc 파일에 PATH 설정을 추가해줍니다.



또는 패키지로 설치하는 방법도 있습니다.




2-2. MAC OS X


맥 OS X에서는 pkg 설치 파일을 받아서 설치합니다.

pkg 파일의 설치 방법은 클릭만 할 줄 알면 되는 부분이므로 따로 설명을 적지 않겠습니다.


pkg 파일이 아닌 tar.gz 파일을 설치하는 방법은 위의 리눅스와 비슷합니다.


하지만 wget 명령이 아닌 curl 명령을 이용합니다.


curl -0 "링크"


그 밑부터는 리눅스와 같습니다.


다만 .bashrc 가 아닌 .bash_profile 입니다.


2-3. Windows


윈도우에서는 msi 설치 파일을 받아서 설치합니다.


그저 다운을 받고 마우스 클릭만 할 줄 알면 되는 부분이므로 따로 설명을 적지 않겠습니다.



설치 하고 난 뒤에 프롬프트에 go 를 입력하여 정상적으로 Go 언어가 실행되는 지 확인합니다.

우선은 오늘은 여기서 마무리 짓도록 하고, 
다음 편에서는 Go 언어의 IDE 추천과 기본 문법을 알아보도록 하겠습니다.


이번 주에는 취약점 검사 방법의 변천사와 현재 트렌드, 핀툴에 대하여 깊게 파고드는 시간을 가졌다.


사실 취약점 검사 방법의 변천사, 현재 트렌드 들은 후반 부 밖에 듣지 못하였다...


시간 계산을 잘못해서 너무 늦게 나와서 20분~30분을 늦어버렸다...


취약점 찾는 기법의 변천사


소스코드 오디팅

[Vulnerable functions]

-> strcpy , memcpy , gets , fges , printf


api usage 탐색

-> cat program.c | grep strcpy


후킹

-> Vulnerable functions에 유저 인풋이 들어가는지 체크


퍼징

-> valid한 유저인풋을 bit flipping이나 구조를 약갘씩 변경해 주면서

   다양한 code path를 탐색하면서 특정 값이 취약한 루틴에 들어갈 때

            발생하는 취약점을 찾는 확률적인 취약점 검사 기법


taint progation analysis

-> 유저인풋의 data flow를 죄다 트래킹해서

   destination, soruce, instruction pointer 등에 영향을 주는 포인트를 알아낸

   tainted destination

   tainted source

   tainted instruction pointer


-> taint == 오염시키다

-> 모든 취약점은 유저인풋으로 나온다

-> hwp.exe exploit.exe

   hwp.exe -> CreateFile | OpenFile(“exploit.exe”)

   -> ReadFile(hFile, buf, 65535); -> tainted : buf

   -> 표를 뜻하는 opcode ==> tainted

  handler_table[opcode](section_data)

  -> call handler_table(, eax, 4) ==> eax : tainted

  -> handler_table + eax*4


dynamic binary instrumentation

 -> Pintool , libdft


symbolic executionn —> KLEE &  constraint solver —> z3

          -> path explosion

 -> scalable 하지 않다는 단점 존재


concolic execution

—> concrete execution + symbolic execution

conc(rete) + (symb)olic


path analysis | 1day analysis

—> binary | source code diffing


vulnerability extrapolation —> fabian yamaguchi

코드 유사도 비교를 통한 취약점 분석

key concept:

기존에 취약하다고 알려진 코드 패턴이 다른 코드베이스에서도 발견이 될 경우 그 코드도 취약할 가능성이 높음

jaccard similarity coefficient algorithm

hausdorff similarity


나는 symbolic executionn 에서 부터 들어서 위에 부분을 자세히 모른다..


젠장 ㅠㅠ;


그나마 뒷 부분에서 알아들은 내용이라곤 코드 유사도 비교를 통한 취약점 분석 부분이였다.


예~~전에 문장 유사도 알고리즘을 발견해서 보면서 구현하고 깃허브에 올린 게 기억이 나는데.


이것은 여러 방법들이 있지만 cd80니뮤 말씀하신 방법은 이러하였다.


(잘 기억이 안나지만 이러했던걸로 기억남..)


하이레벨 코드로 변환해서 그 코드들을 함수에 임의로 지정한 숫자들을 그래프로 표시하여 


원본 소스와 비교할 소스와 그래프로 비교하여 유사도를 확인한다고 했다.


꽤 흥미로웠었다.


그 뒤에는 핀툴에 대해 jinmo123니뮤 가르쳐주셨는데...


솔직히 알아들은게 없었다고 해도 다를 께 없었다..


ㅂㄷㅂㄷ;;


뭐 일단 대충 어떤식으로 동작을 하는 지 그 부분까지는 이해했는데.


그 뒤부터는 귀로 들어오질 않았다...ㅂㄷㅂㄷ;


설치부터 막혀버린 나의 상황이 너무 안타깝다..


뭐 그래도 이번 주는 시간이 워낙 많으니 삽질할 시간이 많아서 


구글링을 통해 사용법을 터득할 예정이다.


터득한 뒤에 자세히 정리를 하여 올려봐야겠다.


일단 설치부터... [a-z] 까지 최대한 적어보자!







그러하다.


이 5주 차에서는 4주 차 과제를 제일 먼저 풀은 2명이 풀이를 발표하게 되었었다.

그러나 1분은 늦잠을 자셔서 못오셨다고..

그렇게 pork 풀이와 한 문제의 풀이를 듣고 5주 차 풀이 발표자 분이 과제를 내게 되었다.


이번 과제는 layer7 ctf 2015에서 나온 문제를 풀기 였다.



문제는 이거다.


무려 이 대회에서 4명밖에 못 풀은 문제다.


사실 이 문제는 건드린 시각은 과제 내용을 들은 일요일이였는데.

월~수가 수학여행이라 과제를 건드릴 시간이 없어서 오늘 적는다...ㅎ;


사실 월~수 중에 숙소에서 과제를 하려고 했는데...

심하게 피곤해서 숙소 도착하면 정신도 못차린 채 쓰러졌다고 ... 한다.




그러하다.

끝.

ROP 심화 부분이라고 한다.


ROP 문제들을 풀면 된다.



풀어야한다...

이번 3주차에서는 ROP를 다뤘다.


ROP(Return Oriented Programming) 

: BOf + 여러가지 ①미티게이션 우회할 수 있는 테크닉


① 미티게이션 : 취약점을 막는 것이 아니라 취약점을 통한 익스플로잇을 막는 것.

ex) strcpy(buf, argv[1])


이러한 미티게이션은 4~5개 정도 있으며 가장 대중 적으로 사용되는 것은 3개가 있습니다.


  1. ASLR
  2. NX
  3. SSP

ASLR(Address Space Layout Randomization)
: 일반적인 bof의 흐름에서는 스택에 쉘코드를 적재해두고 거기로 리턴하는데 스택의 주소를 프로그램 실행 시마다 다르게 합니다.

ASLR 확인 방법 : /proc/sys/kernel/randomize_va_space 값을 확인.

 0

 걸려있지 않음

반 걸려있음 

다 걸려있음 


해결 방법 

1. 스택, 힙, 라이브러리 그렇기 때문에 랜덤화해주지 않는 정적인 주소 값을 찾아서 이용하자.

2. x86 아키텍쳐에 한에서 ulimit -s ulimited 를 사용하면 일시적으로 ASLR이 해제된다. 왜 이러는 것인가?



NX(Non eXecutable stack)

: Write 권한, Executable 권한을 동시에 부여하지 않는다.


해결방법

1. ①RTL을 이용해서 우회를 한다.

① RTL(Return To Libc) : eip를 변조하여 쉘코드를 실행하는 것이 아닌 직접 라이브러리 주소로 점프해 system("/bin/sh") 이나 명령을 다이렉트로 실행한다.


ex) buf -> |    |  SFP  |  라이브러리에 있는 시스템 주소  |  asdf  |  /bin/sh  |


라이브러리에 binsh 문자열이 있다.

아니면 스택이나 정적 영역에 binsh 넣고 문자열을 만들어 주도록 하자.


라이브러리 영역에서 시스템 함수가 내부적으로 호출 될 때 /bin/sh -c 

system("ls"); == /bin/sh -c ls


Tip1. system 주소 알아내는 방법 : p system

Tip2. /bin/sh 주소 알아내는 방법 : find &system, -999999999, "/bin/sh"


|  'A' *68  |  system  |  asdf  | system의 first argv[]  |

   &system   asdf     &/bin/sh 


system을 포함한 다른 함수를 호출 할 때 그 바로 다음 값이 그 함수의 리턴 값이 된다. 왜 이러는 것인가?



PLT, GOT ::


PLT : 바이너리 영역에 있는 건 바이너리에서 사용되는 함수들의 정보만 따로 모아두고 사용하게 편하게 한다.

GOT :  런타임시 라이브러리 주소를 불러와서 저장을 하는데 그게 실제로 저장되는 공간이다.


PTL에서는 호출이 되면 일련의 과정을 통해 GOT에 라이브러리 주소를 불러옴.

CALL PTL :  PLT는 GOT에 저장된 값으로 JMP를 하는데, 이게 처음이면 기본적으로 GOT에는 PLT+6이 저장되있음.

PLT+6에서는 dynamic linker 라는 것을 사용해서 GOT에 라이브러리 주소를 로딩하는데.

이게 처음이 아니면 그냥 GOT로 점프를 할 때 라이브러리 주소가 이미 로드 되어 있으므로 바로 라이브러리 호출.


PLT, GOT의 유용성


1. PLT, GOT는 바이너리 영역에 저장되어 있기 때문에 정적이라 ASLR의 영향을 받지 않는다.

2. PLT를 호출하는 것만으로 바로 라이브러리를 가져다 쓸 수 있다.

3. 바이너리에서 사용하는 모든 함수들을 사용할 수 있다.

 




이 스터디에서는 가르쳐주신 걸 모든 내용을 문서화하면 이쁨을 받는다고 해서 작성했3

그리고 나중에 내가 쓴 걸 다시 돌아보면서 많은 생각을 하게 만들 것이다.


1주 차에서는 어셈블리어 공부, 어셈블리어 프로그래밍, 핸드레이를 진행하였다.


어셈블리어에는 어마어마한 레지스터들이 있다는 것을 알게 되었다.

하지만 우리가 쓰는 레지스터들은 보통 8개 정도 쓰인다고 한다.


범용 레지스터 (General Resgister)


EAX (AX, AH, AL) - 누적 연산기, 곱셈과 나눗셈 연산에서 자동으로 사용한다.

EBX (BX, BH, BL) - 베이스 레지스터, 특정 주소를 지정한다.

ECX (CX, CH, CL) - 카운터 레지스터, 반복 카운터로 사용됨.

EDX (DX, DH, DL) - 데이터 레지스터, 입 출력 연산에서 반드시 간접 주소로 지정에 사용됨.

EBP (BP) - 베이스 포인터, 스택의 데이터에 접근하기 위해 사용됨

ESP (SP) - 스택 포인터, 스택 최상부의 오프셋을 가리킨다.

ESI (SI) - 읽기 인덱스, 데이터 조작, 복사 시에 소스 데이터의 주소가 저장됨

EDI (DI) - 쓰기 인덱스, 복사 시에 목적지의 주소가 저장됨

EIP - 명령어 포인터 레지스터, 실행할 다음 명령어의 주소를 포함

EFLAGS - CPU의 동작을 제어하거나 CPU 연산의 결과를 반영



AH, AL 중 H 는 High 을 뜻하고 L는 Low를 뜻한다.


이제 어셈블리어에 레지스터에 대해서는 잘 끄적인 것 같으므로 다음으로 넘어가보자.


일단 첫 번째로 write를 구현을 해보라고 했다.


이에 대해서는 시스템 콜을 사용해야한다.


시스템 콜이란 커널의 자원을 사용자가 사용 할 수 있도록 만들어 놓은 고급 API? 같은 것이다.


사용자 프로세서가 시스템 콜을 요청하면 제어가 커널로 넘어간다.

커널은 내부적으로 각각의 시스템 콜을 구분하기 위해 기능 별로 고유 번호를 할당해 놓는다.

이것이 바로 시스템 콜 넘버라고 한다.


그에 비해 라이브러리 함수는 사용자들이 많이 사용할 것 같은 기능들을 미리 만들어놓은 것이다.

하지만 이 라이브러리 함수는 사용자 모드에서 실행된다.


그렇다면 우리는 넘겨줘야되는 것은 (system_call, fd, &buf, nbyte) 가 되시겠다.


이제 어셈블리어 코드를 작성해보자.


어셈블리어에서는 지시자들이 있다.


아직 지금까지 내가 써본 지시자들은 .globl, .string 뿐이다.


이에 대해 설명하자면 

.globl (global) <Symbol> : 심볼을 외부 참조가 가능하게 한다.

.string : 오브젝트 파일에 문자열을 복사(?) 

.data : 초기화된 데이터 섹션




지금 알려준 것은 함수에서라고 한다.


카운트를 0으로 초기화해준 뒤에


가져온 eax 를 바이트 단위로 bl 8bits 레지스터에 넣는다. (?) 맞나?


그 뒤에 바이트 단위로 %bl 과 null을 비교를 하고


같을 경우 끝으로 점프한다


아닐 경우 ecx ++, eax++ 하며 루프를 돈다.


※ 아 (%eax) 를 하면 메모리 주소의 값을 의미한다고 한다.

ex)

%eax = 0xffffe008


Memory :

0xffffe008: 0xabcdef


%eax 는 0xffffe008 

(%eax) 는 0xabcdef 

라고 한다.


그렇게 엔딩에 가고 카운트를 eax 에 넣는다.

eax 레지스터는 리턴 값으로 자주 쓰이는 것 같아 보인다.



_M#]


위에는 전에 썻던 글인데 

왜 이렇게 썻는지 모름... ㅂㄷ;;


고로 버리기에는 아까우니 요약에다가 쑤셔넣기 !


어여튼 어셈 코드는 이러하다.


입력을 받고 esi 에 넣는다.


글자 길이를 카운트를 책임질 ecx 를 0으로 초기화해줌


(esi) 글자를 1글자 씩 bl에 넣음


cmpb로 null과 1글자 씩 비교함


je 같을 경우에 .end 로 점프


아닐 경우 ecx ++ 와 다음 글자를 비교하기 위해 esi ++


그리고 loop 로 다시 점프


그리고 null일 경우 end 로 가서 

eax에 ecx를 넣고 끝


함수는 보통 eax로 리턴을 한다고 한다. (정확한 것은 아님)


_M#]





+ Recent posts