꽃미남 프로그래머 김포프가 창립한 탑 프로그래머 양성 교육 기관 POCU 아카데미 오픈!
절찬리에 수강생 모집 중!
프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by 알 수 없는 사용자

  물리엔진이라면 당연히 3D 세계에서나 필요하다고 여기는 때가 있었죠. 그때 갑자기 엄청 신기한 2D 게임 하나가 나옵니다.

<Crayon Physics Deluxe>

  자신이 직접 손으로 그린 그림이 그 모양 그대로 게임세계에서 물체로써 동작하는 아이디어를 보여주었습니다. 해당 물리엔진의 제작자도 Sweet (아아~ 달콤해~) 라며 좋아하시네요ㅎㅎ (링크
)
 

  또한 각종 플래시 게임에서 간단한 2D 물리들을 이용한 게임들이 등장하기 시작했습니다. 그러다가 2D 물리를 이용한  대박 게임이 나왔습니다. 바로 학교폭력근절 친구 앵그리버드(Angry Bird)죠~!! (무려 다운로드 수가 3억 5천이랍니다;;;)


<Angry Bird>


  두 게임에는 공통점이 있습니다. 바로 같은 물리엔진을 사용했는데요. Box2D 라는 물리 엔진입니다.






  이 엔진의 제작자는 Erin Catto 라는 아저씨구요. 현재 블리자드에서 디아블로3 랙돌을 담당하는 것으로 보입니다. (이번 GDC 2012에서 디아블로3 랙돌 관련 발표를 하셨네요. (발표자료 : http://box2d.googlecode.com/files/GDC2012_ErinCatto_Ragdolls.pdf)


< Erin Catto, Twitter: @erin_catto >

  Box2D 엔진은 완전 공개용이구요. 앵그리버드 처럼 초대박이 났어도 에린 까또 아저씨한테는 땡전한푼 안들어가나 봅니다. 앵그리버드 만든 아저씨한테 크레딧이라도 올려달라고 정중하게 말씀드리네요~ㅎㅎ

Erin: “Hi Peter, could you tell me which physics engine Angy Birds uses?”

Peter:
 “Box2D”

Erin:
 “Great. Would you consider giving credit to Box2D in your game?”

Peter:
 “Yes, of course”

Erin:
 “Thank you! By the way, I am Erin Catto the creator of Box2D”

Peter:
 “Great! I would like to talk to you after the session”
  




  Box2D 엔진 소스는 아래 주소에서 받으실 수 있습니다.


  보통 물리엔진을 만들고 싶어서 공개용 소스를 열어보면 방대한 양에 어디서부터 봐야할지 감이 잘 안옵니다. Box2D도 다른 공개 물리엔진 보다 그 크기가 작긴 하지만 처음 입문하는 사람이 보기에는 쉽지 않죠~ (사용자가 아닌 물리 엔진 제작 입문자로써..)

  그래서 에린 까또 아저씨는 순수하게 박스로만 강체 물리를 구현하는 매우 가볍지만 라이트하고 작지만 스몰하고 짧지만 쇼트한 Box2D Lite 코드를 공개해주셨습니다. Box2D 본 엔진과는 전혀 상관없이 독립적으로 실행되는 완전한 2D 물리 예제입니다.


  동영상을 보시면, 기본적인 강체 물리가 정확히 구현된 것을 확인하실 수 있습니다. (동영상 후반부에는 시소도 있고 흔들다리도 있습니다.)


< 실행 동영상, 중간에 난입하는 녹색 박스는 스페이스 키를 누르면 랜덤으로 발사된다.>


  물리 엔진을 처음 공부하시는 분들은 엔진의 전반적인 흐름이 어떻게 흘러가는지 이 Box2D Lite 소스코드부터 시작하시는게 어떨까 추천해봅니다~!!!

 
반응형
,
Posted by 월하
가금 기획자분들 중 엑셀을 사용 할 줄 모르시는 분들이 있습니다.

약간은 믿을 수 없지만 정말 입니다. 신입? 경력? 예외 없습니다.

저도 잘 쓰는건 아니지만 기본 적인 엑셀 함수들을 정리할 겸 해서 해당 포스트를 작성 하였습니다.

아래의 첨부 파일을 열어 보시면 첫 시트에 이렇게 생긴 녀석이 있습니다.




옙. 정말 민망할 정도로 성의 없이 만든듯한 엑셀 시트 입니다. ㄷㄷㄷ

그저 캐릭터 레벨, 직업, 종족, 장비를 선택 하면 최종 스텟이 출력 되는 형태 입니다.

외관상 볼때는 정말 별볼이 없어 보이는 그런 엑셀 문서 이지만...

제가 약 5년( 현재 만 4년 9개월)간 실무에서 계산을 위해 사용한 대부분의 수식이 들 있는거 같습니다.

첫 시트에 사용된 기능들 부터 알아 보자면 다음과 같습니다.

  1. 콤보 상자


시뮬레이터 / 툴에서 매우 자주 쓰이는 녀석 입니다.

만들기도 쉽고 보기에도 아주 깔끔해져서 개인적으로 상당히 좋아하는 기능 입니다. 

사용 방법은....

 개발 도구 -> 삽입 -> 콤보 상자 를 원하는 위치에 적당한 크기로 그리시면 생성 됩니다.

 


물론 요녀석은 껍데기 입니다.

안의 내용물을 보자면  콤보상자 우클릭 -> 컨트롤 서식 -> 컨트롤 탭을 보시면 됩니다.

그럼 레벨의 컨틀로 서식을 보자면...


이렇게 되어 있습니다.

이게 뭔 말이냐면....

레벨이란 입력 범위내의 값콤보 상자에서 선택 할 수 있게 하면서

해당 값의 데이터를 C4 셀에 입력을 하여 주는 겁니다.

그럼 여기서... 대체 입력 범위에 레벨이 무엇인가... 하는 의문이 들 수 있습니다.

여기에 사용된 레벨 이름 정의 기능을 통하여 사전에 정의된 규칙 이름 입니다.


  2. 이름 정의


이름 정의는 엑셀에서 특정 범위의 데이터를 편하게 사용 하기 위해 특정한 단어로 규약 하는 것을 말 합니다.

가령 첨부된 엑셀 파일에서 ref 시트를 보시면


 이런 부분이 있습니다.

여기서 붉은색으로 박스를 친 부분이 해당 엑셀 문서에서 이름 정의된 부분 입니다.

이름 정의를 하는 법은 간단 합니다.

원하는 범위를 드래그로 선택 한 후  수식 -> 이름 정의를 클릭 하시면 됩니다.

그럼 아래와 같은 창이 호출 됩니다.


참조 대상이 방금 선택한 범위이며 이름에 적당한 이름을 쓰시면 됩니다.

단, 이름에는  공백이나 특수 문자는 사용 할 수 없습니다.(언더바 제외)

이렇게 정의된 이름은 아래와 같이 이름 관리자 ( 수식 -> 이름 관리자)에서 볼 수 있습니다.


참조 대상에 뭔가 이상한 수식들이 보이지만 일단 나중으로 넘기고 진행 하도록 하겠습니다.


이름을 정할때는 신중히 정해야 합니다.


이름을 정하는건 정말 고민 되죠. (?)

뻘 짤방이고..... 엑셀에서 이름 정의가 중요한 이유는....

수식이나 기타 등등에서 범위값을 일일이 지정 하는것보다 미리 정의된 이름을 넣는게 편하기 때문 입니다.

그런데 이름이 복작하거나 헷갈린다면?  안하느니 못한거죠.



여하튼 위 이미지에 테두리가 줄러진 녀석을 이름 정의한 범위 인데 각

레벨 / 종족_레벨 / 직업_레벨의 3 가지 범위를 이름으로 지정 하였습니다.

이미지에 테두리가 잘못 둘러져 있는데 레벨 부분에 "레벨"이란 글자는 범위에 포함이 되지 않았습니다.


즉, 아까 본 이 녀석은 1 ~ 50 까지의 입력 범위를 가지는 것입니다.

콤보 박스에서 레벨을 1~50 까지의 레벨 중 하나를 선택 하면 해당 값이 C4 셀에 입력이 되는 녀석 입니다.

이제 본격적으로 셀을 하나 하나 뜯어 보도록 하겠습니다.

먼저 C4 물리 공격의 스테이터스가 출력 되는 부분 입니다.


여기선 간단한 두 개의 함수가 사용 되었습니다.

  3. IF 수식

정말 중요한 수식 입니다.

아마 엑셀을 다루다 보면 정말 질리도록 사용 되는 수식이죠.

기본 구조는 다음과 같습니다.

=IF(조건 , 조건이 참일 때 값 , 조건이 거짓일 때 값)

위의 수식은.....

조건: C4=1

조건이 참일 때 값: ref!E3

조건이 거짓일 때 값: ref!E3*((VLOOKUP(G4,종족_레벨,3,FALSE)^(C4-1))*((VLOOKUP(E4,직업_레벨,3,FALSE)^(C4-1)))) 

입니다.

즉, 조건인 c4(레벨)가 1이면 ref 시트의 E3 셀의 값을 받아 오고 그렇지 않으면 저 수식의 값을 출력 합니다.

해당 수식에 포함된 vlookup은 다음과 같은 함수 입니다.

 4. vlookup 수식

vlookup의 수식도 마찬가지로 아주 중요한 수식 입니다.

if 못지않게 지겹게 사용 되는 수식인데 구조는 다음과 같습니다.

=vlookup(찾을 값 , 데이터가 들어 있는 셀 범위, 반환해야 할 테이블의 인수 열 변호, 옵션)

음... 이렇게만 해서는 뭔 말인지 알아먹기가 더 힘드네요.

위의 예제 중 하나만 뜯어서 살펴보면...

VLOOKUP(G4,종족_레벨,3,FALSE)

찾을 값: G4

데이터가 들어 있는 셀 범위: 종족_레벨(이름 정의된 범위)

반환해야 할 테이블의 인수 열 변호: 3

옵션: false

이란 구조인데 간단히 설명 하자면...

종족_레벨 이라는 셀 범위에서

G4에 해당 하는 값을 찾은 후

해당 값을 기준으로 우측으로 3번째 있는 값을 찾는 것입니다.

여기서 옵션인 false는 정확히 일치하는 값을 찾는 것입니다.

다른 옵션으로는 true가 있는데 근사값을 찾을 때 사용 됩니다.

자세하 예제는 http://excel.officeapps.live.com/x/_layouts/xlEmbed.aspx?C=1__SKY-WAC-WSHI&su=8247236622081652475&Fi=SD7274120315C4F6FB!361&AllowInteractivity=True&AllowTyping=True&lc=ko-KR 

를 참조 바랍니다.


즉 위의 수식

 =IF(C4=1,ref!E3,ref!E3*((VLOOKUP(G4,종족_레벨,3,FALSE)^(C4-1))*((VLOOKUP(E4,직업_레벨,3,FALSE)^(C4-1)))))

의 경우 1레벨 이면 ref시트의 E3을 가져오고

1레벨이 아니면 ref시트의 E3에 종족에 해당하는 가중치를 레벨만큼 곱하고 직업에 해당하는 가중치를 레벨만큼 곱해주는 겁니다.


 번외. 데이터 관리


다음 설명 들어가기전에 잠깐 data 시트를 보겠습니다.

굉장히 단순한 구조의 아이템 데이터 입니다.

인덱스 / 이름 / 종족 / 아이템 파츠 / 그리고 각 스텟.......

제일 마지막에 계산용은 시뮬레이터용 입니다. 실제 데이터에는 없죠.

그럼 먼저 A2 시트를 확인해 보겠습니다.

이건 뭐 회사마다, 작업자마다 조금 스타일이 다른데 제가 개인적으로 선호하는 방식 중 하나 입니다.

(종족 인덱스 * 1000000) + (파츠 인덱스 * 10000) + 종족별, 파츠별 등록 번호 입니다.

이렇게 하면 인덱스 번호만 봐도 종족과 어느 파츠의 아이템인지는 쉽게 추적이 가능 하기 때문입니다.

그리고 계산용에 있는 값은 종족 인덱스 * 10 + 파츠 인덱스 입니다.

그럼 이제 가장 중요한 계산 시트를 보겠습니다.



간단하게 위와 아래로 구분을 하겠습니다.

위의 경우는 데이터를 입력 받기 위한 범위값의 설정을 위한 부분이고

아래는 계산기 시트에서 선택한 실제 아이템의 데이터를 호출하는 부분 입니다.

위 시트부터 확인 해 보자면...

A2번은 계산기 시트에서 선택한 종족의 인덱스가 호출됩니다.

B2:B7은 수동으로 입력된 값입니다.

C2:C7은 다음의 함수가 사용 됩니다.

C2를 예로들면....

=VALUE(CONCATENATE($A$2,B2))란 수식이 있습니다.

 5. value 수식

value는 사용 빈도는 낮지만 알아두면 의외로 좋은 수식입니다.

굉장히 단순한 함수인데 그 내용은

value(데이터) 에서 data가 텍스트일때 이를 숫자로 인식 할 수 있습니다.

음... 사실 이 함수가 단독으로 쓰이는 일은 거의 없습니다.

위의 예에서 처럼 CONCATENATE라 mid, left, right등의 함수랑 복합해서 사용 합니다.

CONCATENATE라 mid, left, right 같은 함수는 텍스트 함수로 실제 값이 숫자이더라도 해당 함수로 출력된 값은 텍스트로 인식해 버리죠.

이럴때 다시 숫자로 변환 해줄 수 있는 함수 입니다.

 6. CONCATENATE 수식

CONCATENATE는 두 개의 셀을 하나의 값으로 합쳐주는 수식 입니다.

위에서 사용한 CONCATENATE($A$2,B2)의 경우 A2와 B2의 값을 하나로 묶어서 출력 하는 거죠.

즉, A2의 1과 B2의 1을 11이라는 텍스트로 호출하는 함수이고 5번에서 언급한 value 함수로 인하여 숫자 11 로 호출 합니다.


이제 D2와 E2에 쓰인 match 함수를 알아 보겠습니다.

 7.  MATCH 수식

match 함수는 해당 데이터가 테이블(목록)에서 몇 번째에 위치하여 있는가를 찾는 함수 입니다.

가령 A에서 Z까지 있는 목록에서 C가 몇 번째에 있는가.... 같은것을 구할때 사용 합니다.

D2에는 =MATCH(C2,data!$L:$L,0) 라는 수식이 있습니다.

이 수식은 C2의 값이 data시트의 L열에서 몇 번째에 있는지 찾아주는 수식 입니다.

여기서 C2의 값은 종족&파츠의 값 입니다.

그러니 data에 중복되는 값이 많이 있죠.

여기서 D2와 E2의  차이가 들어 납니다.

D2는 =MATCH(C2,data!$L:$L,0) 이고
E2는 =MATCH(C2,data!$L:$L,1) 입니다.

0과 1의 차이 인데 0의 경우 일치하는 첫번째 위치를, 1의 경우 일치하는 마지막 값의 위치를 찾아 줍니다.

그렇기 떄문에 D2의 값은 2가 되고 E2의 값은 22가 됩니다.


이제 인간 헤어의 첫번째 열과 마지막 열을 알았으니 인간 헤어 데이터 범위 값이 나왔습니다.

이름을 기준으로 보면 B2:B22 입니다.

 7.  ADDRESS 수식

이제 이름의 범위값을 알게되었습니다.

하지만 엑셀은 알 수 없죠.

엑셀이 알 수 있도록 하는 작업이 필요한데 이때 사용하는 수식이 ADDRESS입니다.

Address수식은 다음과 같이 구성 됩니다.

Address(행번호, 열번호, 유형, 스타일, 시트 이름)

입력된 행번호와 열번호에 맞는 셀을 지정된 유형과 스타일에 맞게 시트이름과 함께 호출 하는 형태 입니다.

여기서 유형에 대해 알아보자면

 1 또는 생략 절대 셀 
 2 절대 행, 상대 열
 3 상대 행, 절대 열 
 4 상대 행, 상대 열

이 됩니다.

그리고 스타일의 경우

true이거나 생략할 경우 AI 스타일을 false일 경우 R1C1  스타일을 사용 합니다.

일반적으로 사용 하는 스타일이 A1 스타일 입니다.

R1C1 스타일을 사용 하는 경우는 거의 없을겁니다.


가령 address(2,2,4,,,)이면

행번호 2 / 열번호 2 / 상대행, 상대열 / 스타일 AI / 시트 이름 생략

이느 B2 셀이 호출 됩니다.

그럼 G2 수식을 살펴보면

=ADDRESS(D2,2,,,"data")&":"&ADDRESS(E2,2)

이라고 되어 있습니다.

ADDRESS(D2,2,,,"data") 와  :  텍스틑, ADDRESS(E2,2) 을 붙여서 표기 하도록 되어 있씁니다.



즉, data시트의 D2값에 해당하는 행과 2번 열을 절대 셀 유형으로 표기 하고 

E2값에 해당하는 행과 2번열을 절대셀 유형으로 표기하는 겁니다.

이렇게 되면 data!$b$2:data$b$22라는 값이 호출 됩니다.

데이터 시트의 B2에서 B22까지의 범위를 나타내는거죠.

하지만 이는 텍스트로만 사용되지 실제 테이블로는 사용 할 수 없습니다.


그래서 이름 관리자에서 indirect라는 함수가 사용 되었습니다.

 8.  INDIRECT 수식

indrect의 경우 해당 값을 참조 하는 기능 입니다.

즉 이름이 헤어인 항목을 보면 indirect(계산! $G$2)이라고 되어 있는데 이는

무기의 참조 대상은 G2에 입력된 값이 지정하는 범위를 참조 하라는 겁니다.

즉, 헤어의 참조 범위는   data!$b$2:data$b$22 가 됩니다.

 9.  끝

이상으로 해당 엑셀 문서에서 사용된 대부분의 기능을 다 다루었습니다.(빠진게 있는지 잘 모르겠지만)

솔직히 엑셀 수식의 경우 도움말에 정말 설명이 잘 되어 있습니다.

충분한 예제와 같이 설명이 되어 있죠.

엑셀 수식은 조금만 관심을 가지면 쉽게 익힐수 있습니다.

하지만 엑셀 수식은 단독으로 쓰이는 경우보다 2개 이상의 수식이 복합적으로 사용 되는 경우가 많습니다.

가령 COUNTIF의 경우 테이블 내에서 특정 조건을 만족하는 숫자들의 합을 구합니다.

예를 들면 이름, 원산지, 과일멸, 가격의 테이블에서 원산지가 국산인 과일들의 가격 합을 구할 수는 있습니다.

하지만 국산 사과 가격의 합을 구할 수는 없죠.

이럴 때는  SUM(IF((B:B="국산")*(C:C="사과"),D:D) 같은 형대로 사용 해야 합니다.

이토록 엑셀 함수의 묘미는 두 개 이상의 수식이 복합적으로 사용 될 때 입니다.

하지만 이런건 도움말에 잘 안나오죠.

검색이나 다른 사람 물어 보는게 빠르고 편합니다.

그러니깐.....

다들 vba 배우세요. vba가 짱이에요.  

 

반응형
,
Posted by 친절한티스
객체 지향 방식에서의 데이터 처리


 
위의 Player 클래스는 그 하나만 놓고 보자면, 클래스 1개로 보여지지만, 상위 클래스로부터 파생된 만큼 상위 클래스의 데이터를 모두 가지고 있습니다. 상위 클래스들을 모두 합쳐 놓은 모습이라고 볼수 있습니다. Player 클래스가 보유 하고 있는 데이터들을 간략하게 표현 한다면 밑의 그림과 같을 것 입니다.


이 Player 클래스는 매 프레임 마다 자신의 상태 갱신을 수행하게 됩니다. 상태 갱신은 주로 자신의 클래스로 부터 상속 받은 상위 클래스를 따라 순차적으로 진행 됩니다. 먼저 Player 상태가 업데이트 되고, 이동과 현재 위치 계산을 위한 MovableObject 클래스, 화면에 오브젝트를 그리기 위한 RenderableObject 클래스, 마지막으로 GameObject 클래스 순으로 업데이트 됩니다.


  위의 업데이트 과정에서 Player 클래스의 데이터들이 처리 되는 방식을 살펴 보겠습니다. 이와 같은 객체 지향 프로그램에서는 주로 객체 단위로 데이터를 처리합니다. Player 클래스가 여러개 있다면 각 Player 클래스 마다 위의 그림 처럼 데이터를 가지고 있을 것이고, 이 데이터들이 Update() 흐름도 처럼 순차적으로 처리가 됩니다. Player 캐릭터의 상태 데이터, 현재 위치 데이터, 출력을 위한 애니메이션 데이터 식으로 말입니다.


구성 요소 기반 객체 방식에서의 데이터 처리


구성 요소 기반 객체에서는 데이터를 컴포넌트가 관리합니다. 객체 지향 방식에서와 다르게 구성 요소 기반 객체 방식에서는 Player 클래스가 따로 존재 하지 않으며, 기본 객체 클래스에 HealthComponent, MoveComponent, RenderComponent 등의 컴포넌트를 조합하여 위의 Player 클래스와 같은 형태의 객체가 생성되는 방식입니다. 데이터들은 각 컴포넌트에 캡슐화 되어있고, 이 데이터들은 객체가 아닌 컴포넌트에 의해서 처리가 됩니다.


 각 컴포넌트들은 카테고리로 분류 되어있습니다. 위치나 애니메이션이 갱신 되기전에 먼저 그려지면 안되는 것 처럼 처리 순서를 지켜주기 위해 분류를 해두고 있는 겁니다. 이 분류는 컴포넌트 매니저 같은 관리자 클래스에 의해 관리됩니다. 각 관리자 클래스는 자신이 관리하는 종류에 맞는 컴포넌트들을 컨테이너에 가지고 있으며 이를 이용해 컴포넌트들을 갱신합니다. 

 
컴포넌트 처리 순서를 지키기 위해 Logic Component Manager에 의해서 먼저 Health Component 들이 갱신 되고, 그다음 Move Component Manager를 통해 통해 Move Component들이 갱신 되는 식으로 동작을 하게 됩니다. 여기서 객체 지향 방식과 큰 차이가 납니다.

일괄된 데이터 처리의 강점
다시 위의 객체 지향 방식의 데이터 처리 방식을 상기해주시기 바랍니다. 객체가 갱신 되면서 내부 데이터들을 처리 하게 되는데, 보시면 각기 서로 다른 데이터가 같이 있습니다. 그에 반해 구성 요소 기반 방식의 데이터들은 관리자 클래스에 같은 종류의 데이터들이 연속된 형태로 묶여 있는 것을 확인 할수 있습니다. 여기서 구성 요소 기반 객체의 병렬 처리 강점이 다시 한번 드러나게 됩니다.

CPU는 데이터를 읽을 때 데이터를 캐쉬에 올려놓습니다. 만약 캐쉬에 데이터가 없으면 캐쉬 미스가 일어나 메모리, 디스크에서 데이터를 읽어들이게 됩니다. 그런데 메모리나 디스크를 통해 데이터를 읽어오게 캐쉬에서 데이터를 읽어 오는 것보다 몇십배 되는 시간이 소요 됩니다. 캐쉬 미스가 자주 일어나게 되면 퍼포먼스가 그만큼 떨어지게 되는 것 입니다.


데이터가 캐쉬에 올라올때는 보통 해당 데이터의 주변 데이터를 같이 불러오는 경우가 많습니다. 여러 경우가 있겠지만 보통 근처에 있는 데이터가 사용될 확률이 높기 때문입니다. 이 때문에 같이 처리될 데이터를 배열 같은 방식으로 한데 묶어 사용하게 되면 그만큼 캐시 미스가 적게 일어나게 됩니다.

다음 이미지는 다이스(Dice)에서 DOD (Data Oriented Design) 라는 데이터 중심 디자인에 관한 발표 자료 중 일 부분입니다. 봇(Bot)이라는 클래스를 예를 들어 봇이 타겟을 조준 하는 과정이 4번 일어난다고 했을 때 캐쉬 미스로 인해 지연율(Latency)이 얼마나 생기는지를 나타내고 있습니다.



함수가 호출되면서 명령어 캐싱이 일어나고, 그 다음 함수 내에서 사용되는 값들이 캐싱 됩니다. 함수 호출이 총 4번이 일어나므로 캐싱도 4번 반복을 합니다. 총
 7280 사이클이 소요되었습니다. 그에 반해 동일한 데이터를 한데 묶어 일괄적으로 데이터를 처리하게 되면 어느정도 성능이 향상되는지 살펴보겠습니다. 



바뀐 부분을 보시면 기존에 함수를 4번 호출해서 처리하던 데이터를 배열로 만들어 동일한 데이터를 묶어 일괄적으로 처리 할수 있도록 변경한 것을 확인 할수 있습니다. 덕분에 캐시 미스가 줄어들고, 단지 1980 사이클 소요만으로 데이터 처리가 끝났습니다. 장점은 이것만 아닙니다. 위 코드를 잘 보면 이 코드는 루프에 대해 병렬적인 것을 확인 할수 있습니다. 기존의 코드에 비해 병렬성이 증가한 것입니다.

이처럼 동일한 데이터를 한데 묶어 처리하게 되면 많은 장점이 생깁니다. 객체 지향 방식에서 이동 처리, 애니메이션 처리 등을 한데 묶어 처리하게 되면 데이터 의존성 때문에 병렬화가 힘들지만, 구성 요소 기반 방식에서 같은 데이터만을 묶어 처리 하게 되면 의존성도 줄어들어 병렬 처리가 수월해짐과 동시에 성능도 올릴 수 있게 됩니다.



참조
게임 오브젝트 설계.. 나도 잘하고 싶다! by 끼로
프로그래머가 몰랐던 멀티 코어 CPU 이야기 김민장님저
Introduction to Data Oriented Design by Dice 발표 자료
Multiprocessor Game Loops Uncharted2 by Naughty Dog 발표 자료
반응형
,

만남의 자리!

기타 2012. 3. 8. 18:29
Posted by 알 수 없는 사용자

주최측(.Pope...)의 동의를 얻어서, 약간의 광고성 글을 올립니다요~ 냠냠...


당신들이 생각하는 이런 만남은 아니구요... (이런 늑대같은 인간들.... )


제가 "카사"라는 스터디 모임을 하고 있는데요.
올해는 무엇을 해볼까? 라고 고민하다가 "외부강연 해보자!"  라는 생각을 했었습니다.

어떻게 보면, 일종의 재능 기부 느낌도 있는데 그렇게 거창한 것은 아니구요. 어째든 어느 정도 경험있는 개발자들과 이야기를 나눌 수 있는 기회가 전반적으로 부족한 것은 사실이니, 우리가 할 수 있는 선에서 그런 기회를 만들어보자! 라는 취지였습니다.

특히, 게임을 공부하고 있는 학생들은 현업 개발자와 만날 수 있는 기회가 있으면 엄청 좋을 것 같다고 생각했지요...

1월부터 블로그와 트위터 등으로 공지를 했지만, 많이 알려지지 않아서 아직 한번도 실행해보지는 못했네요. ㅎㅎ.

포프 아저씨가 한 칸 정도 광고해도 된다고 해서, 용기내어 홍보글을 올려봅니다.

"카사 스터디"에 대해서는 모임 소개를 보시면 되겠습니다만, 기본적으로는 프로그래머가 중심의 현업개발자 커뮤니티라고 보시면 되겠습니다.  

저희도 이런 행사를 해본 경험이 없어서 어떤 식(아마도 강연형식이 되겠죠)으로 진행할지는 연락을 통해서 조율하면 좋을 것 같고요. 기본적으로 금액은 없습니다. (단, 굳이 밥을 사주신다면 감사히 먹겠습니다. ㅎㅎㅎ)


현업 개발자들 (KASA 사람들이겠죠!) 의 이야기를 듣고 싶은 곳(대학, 고등학, 아케데미 등)이 있으시면, 연락(cagetu79@gmail.com)을 주시면, 팀원들과 상의해서 최대한 긍정적으로 검토해보도록 하겠습니다.

끝! 


대강 이런 이야기 입니다. 

앞으로 어떻게 될지도 모르겠지만, 일단 이런 어처구니 없는 행사를 도전해봅니다. 1회라도 성공하다면, 그 후기를 올려보도록 하겠습니다. ㅎㅎ

재밌겠죠?! ^^

이상으로 홍보를 마치겠습니다.  
반응형
,
Posted by 알 수 없는 사용자
안녕하세요 코인1입니다. 멋진 게임을 소개하려다가 생각나는 것이 있어서 써볼까 합니다. 어쩌면 게임 만들기보다 더 중요한 일이...될지도?



<South Park> Make Love, Not Warcraft (season 10, episode 8)


먼저 질문이 있습니다.

- 게임 개발에 종사하시는 분들은 게임을 자주 하십니까?
- 최근에 엔딩 본 콘솔 게임은 있습니까? 일 년에 몇개나 됩니까?
- 온라인 게임 하십니까? (자/타사 불문) 만렙을 찍거나 그 이상 컨텐츠를 즐기고 있는 게임이 지금도 있습니까?

그럼 또 질문.

- 출시한 게임이 누구를 대상으로 얼마나 팔렸는지 알고 있다
- 동네 피씨방에 가본 일이 있다

아마도 마지막 질문입니다

- 내가 참여한 경험이 있는 게임은 재밌는 게임이다
- 내가 참여한 경력은 가족이나 친척, 이웃에게 자랑할 수 있다
- 내 자녀와 함께 내가 만든(참여한) 게임을 같이 하고 싶다

셧다운제, 쿨링오프에 대한 얘기가 아닙니다. 저런 일련의 제도와, 단순화된 게임에 대한 시각과 그를 바로 잡으려는 탁상행정이 바보같은 것은 분명한 사실입니다. 저 또한 저런 제도에 반대하지만 그 이유는 민주주의에 반하고 자유와 기본권에 대한 심각한 제재라고 생각하기 때문입니다. 그럼 하고 싶은 이야기는 무엇이냐면,

우리가 하고 있는(만들고) 게임이 정말로 즐거운 게임인지,
더 직설적으로 말하자면 정말 게임에는 문제가 없는데 괜히 정부가 돈 뜯으려고 하는 것인지
이런 것들을 생각해 보자는 말입니다.

◇ 정말 중독이 없는가?
게임에는 중독성(안 좋은 말이지만)이 있을까요 없을까요? 없다고 믿고 계시는 분들도 있고 있지만 조절 가능하다고 생각하는 분들도 계실겁니다. 의견은 다르겠지만 우선 제 생각은 있다. 입니다. 98년인가 99년인가에 pc방에서 개강 후 한 달째 피씨방에서 숙식을 하던 대학생, 애기를 안고 와서 집을 제쳐두고 게임에 열중하던 남편의 피씨방 요금을 적금을 깨서 지불하고 데려간 일에, 누군가는 울티마 온라인 하다가 회사 짤리고 이혼하고 그 때는 웃으면서 얘기했는데 등등.. 게임이 중독성이 없다는 것은 재미가 없다는 말과 같지 않을까요.

좀 더 솔직해져 봅시다. 카더라는 싫지만 어쩔수가 없네요. 투자자에게 투자 받으려고 문서에 '이러이러해서 중독성 짱' 대충 이렇게 써서 투자 받는다고들 하는데, 혹시 이런 쪽에 관여된 높으신 분 계신가요? 적어도 이 책 의 저자는 자신을 그렇게 소개하고 다녔다고 하더군요. (저 책은 물론 우스꽝스런 책입니다만) 솔직히 저라도 대충 저런 비슷한 말로 유저를 많이 끌어 모을 수 있다고 쓸 것 같습니다.

그래도 이렇게 말하는 분이 계시네요 (설령 립서비스라도)



◇ 중독 극복 사례
http://harawish.egloos.com/1650821
http://sungmooncho.com/2012/02/29/game-addiction/

인터넷을 뒤져보면 저렇게 훌륭한 극복 사례도 보입니다. 오락실 시절 이야기와 모뎀 머드 시절 이야기만으로는 역부족입니다. 요즘 청소년의 극복 사례가 필요한데 (기왕이면 극적이게 명문대 입학 사례로) 찾기가 쉽지가 않네요.


◇ 요즘 게임과 청소년의 세계
게임과 게임을 이용하는 애들의 세계에 대해 잘 안다고 생각하시나요?

우리 현실에서 게임 캐릭터의 레벨은 현실 세계의 나의 레벨이 되지는 못합니다. 고작해야 군대시절 까지는 가능하겠죠. 온라인 게임 지존이라고 해서 사회 지존이 되는 것은 아닙니다. (큰형님이 될 수는 있겠네요) 하지만 애들은? 애들은 성인하고는 다릅니다. 무언가 특별한 것이 자신과 조금만 연관이 있어도 반에서는 인기인이 되고, 골목에서 대장이 됩니다. 그런 수단은 아주 작고 사소한 것 마저도 영향을 미치게 되는데, 게임이 그 중 한가지입니다.

저연령 대상 모 게임이 초등학생들 사이에서 '반장 스펙' 이 되었다는것 정도는 들어보셨을 겁니다. 그러니까 게임 레벨이 애들 사이에서는 진짜로 현실 레벨이 되는겁니다. 그걸 위해서 부모님들이 대신 레벨을 올려준다는 이야기도 들리더군요. 친구 계정 해킹 당해서 아이템 날린 것 때문에 누군가가 목숨을 잃었습니다. 어른의 관점에서 보면 '고작 게임'이 애들 사이에서는 삶의 연장, 그 자체인 셈입니다.




만약 아이들의 학급 생태계가 게임을 위주로 구축 되었다면, 아이들은 거기에 따를 수 밖에 없습니다. 게임이 아니라면? 백금 샤프라도 사야겠지요. 유행에 따르지 않으면 결국 친구들과도 어울리지 못하기 때문에 기가 죽어 집에 돌아오게 되고, 그렇게 되면 부모님들은 어쩔 수 없이 지갑을 열거나 게임을 하게 해줄 수 밖에 없습니다. 외국은 둘째치고, 한국의 성인들은 단체 사회에서 몸을 빼는 일이 가능합니까? 운동부나 대학원 연구실에서, 또는 회사에서 자기 방식대로 했다가는 무슨 꼴을 당할지는 쉽게 상상하실 수 있을겁니다. 결국 애들 학교에서도 마찬가지가 되는겁니다.

콘솔 게임은 게임 하나를 100시간 하기가 상당히 어렵습니다. 롤플레잉 게임도 30시간 정도면 엔딩을 볼텐데, 온라인 게임은 100시간은 우습지요. 애써 만든 컨텐츠도 그분들 손에 들어가면 금방 바닥납니다. 기본적으로 게임 하는 시간이 무척 깁니다. 끝판왕 깨면 끝나는게 아니라 손에 쉽게 넣기 어려운 강력한 아이템과 보스몹보다 더 재밌는 PvP 같은 경쟁 요소는 게임을 손에서 떼기 어렵게 만듭니다. 여기서 끝이 아니라 청소년용 리니지라고 불리는 어떤 게임은 레어 아이템을 얻을 수 있는 일종의 복권 캐쉬템을 팔더군요.너도 나도 강해지기 위해서 캐쉬를 깝니다. -여담이지만 'KBS 호루라기 할머니 때리는 손자' 편에서 손자가 할머니를 위협해서 문화상품권을 사서 저걸 긁는 것 같았습니다. 사용한 상품권 두께가 족히 백만원권 두께는 되어보였고요.

◇ 현실



저런 구조로 되어있는 요즘 게임을 얼마만큼 즐겨보셨습니까? 나는 와우 공대 뛰면서 회사에서도 잘 나간다, 라고 하시면 몸은 괜찮으신가요? 하지만 간과하지 말아야 할 것은 저 위에 게임 중독에서 벗어났던 사람들도 그렇고, 지금 회사에서 일을 하고 계신 여러분들도 그 목적을 이루기 위해 무언가를 다 접어두고 국영수 위주로 하루에 잠은 7시간 자고 공부에 매달렸던 시기가 분명히 있었을 것입니다.

그렇지만 우리에게 돈을 벌어다 주는 유저, 이른바 충성 고객들은 과연 어떤 모습일까요? 자신의 인생을 맞바꿔 게임을 플레이하고 있습니다. 그런 사람들 하나 하나의 시간과 돈이 모여 캐쉬템 유료화 기획한 분의 승진이 되고 인센티브가 되는 것입니다.



그런 와중에 현재 게임 개발에 종사하는 분들의 게임을 대하는 태도는 어떻습니까? 저는 트위터 같은 곳에서 누구누구 게임이 동접을 몇 만 찍었다더라, 매출이 얼마 나왔다더라 하는 말은 자주 봤는데 무슨 게임이 어떻게 재밌더라 하는 이야기는 거의 보지 못했습니다. 그냥 제가 못 본것이거나, 스타트업 하는 사업가 트윗만 본 것이라고 생각을 합니다.


얼마전에 본 게시물입니다.
정말로 이렇게 생각하시나요?

그렇다면 이제 게임이 누군가의 삶에 어떻게 영향을 미치는지 한번 되물어 봅시다. 그 영향을 정말 몰랐는지, 혹은 관심이 없다든지, 또는 알고는 있는데 그냥 모르는 척 하는 것이라든지 어느 쪽이든 결론은 나올겁니다.

이런 부분에 대한 고려 없이 셧다운제/쿨링오프에 반대만 하게 되면 결국은 어떠한 방향으로든 트집을 잡히게 될 것이고 게임에 대한 편견은 그대로 이어져 나갈 것입니다.

◇ 걱정거리



현재 업계는 거의 무대응으로 일관하고 있습니다. 긍정도 부정도 하지 않고 있고, 정책에 대한 우회로만 만들어 나가고 있지요. 대체 왜 그럴까요? 누군가 고양이 목에 방울을 달아주기만을 기다리고 있습니다. 셧다운제라는게 생기기 이전에 자진해서 피로도 시스템을 확대하거나, 부모님 주민등록 번호 도용 전수조사 해서 부모님에게 직접 통보하고 동의 받아내거나 하는 이런 자정에 대한 일들을 누구 먼저 하나라도 앞장서서 해나가서 신뢰를 얻으려고 했다면 게임에 대한 사회 인식이 최소한 떨어지지는 않았을 것입니다.

가장 걱정되는 것은 학부모를 적으로 삼는 최악의 케이스가 되는겁니다. 만약에 학부모들이 연대해서 단체로 게임 불매운동을 벌인다거나 한다면? 아예 어떠한 형태로 어린이 pc방 출입금지같은 법이라도 생겨난다면? 당장 피씨방에서 금연법만 추진한다 그래도 망하네 어쩌네 하는 이야기가 나오는데 아마 여러 사람 거리 밖으로 나앉을 것입니다.

긍정적으로 생각해 봅시다. 매출은 늘지 않더라도 게임을 하는 사람들의 인생을 조금이라도 생각해 주는 이라도 한다면, 그래서 우리 아이 삶에 게임이 도움이 되는 것이라고 학부모가 판단하게 된다면? 셧다운제 같은 법안 만든다고 했을 때 학부모들이 연대해서 아마 여성부에 항의했을 지도 모릅니다. 왜 우리 아이 스트레스 해소하는데 도움되는걸 빼앗아 가느냐고요. 저 닌텐도 조차도 패미컴으로 미국 진출할 때 장사가 안되서 '이거 교육에 좋아요' 라고 사기 친 덕에 지금의 위치에 오르게 되었지요. NDSL 조차도 처음에는 뇌단련이니 뭐니 하면서 좋은 인식으로 장사를 출발한게 지금에 이르게 된 것이라는걸 생각해보면 결코 우습게 볼 만한 것이 아닙니다. 미래를 위한 또 하나의 투자가 될 수도 있지요.

또한 지금의 충성 고객님들이 훗날 결국 아무것도 남지 않았던 (계정 정리하면 돈은 남겠네요) 과거를 회상하며 '그때 게임 줄이고 공부할걸...' 이라고 마음 먹고 자신의 자녀에게 일절 게임을 가까이 하지 않게 한다면? 결국은 미래의 고객을 잃게 되는 샘입니다.

기업은 그럴 필요가 전혀 없다! 그냥 돈 잘 벌어 세금만 내도 국가에 기여하는 것이다! 라고 생각하시는 분은...

전세계 나쁜기업 랭킹

저렇게 되고 맙니다. 그래요. 최소한 쟤네들은 수많은 사람들의 생계를 책임지고 있으니 누가 건드리지도 못하지요. 그런 의미에서라도 사회 공헌은 필요합니다. 그런데 우리 게임 기업은 모다?

이런 과정이 심화되면 언젠가는 세무조사 들어가서 털리거나, 혹은 메가업로드 처럼 사냥을 당할지도 모릅니다.

◇ 결론
과거 컴퓨터가 게임기에 못미치던 시절 게임 잡지의 공략 말미에는 늘 한글 게임에 대한 갈망으로 가득했었습니다. 어스토니시아 스토리가 나오고, 폭스레인저가 나오더니 지금은 세계 게임계에서 한국이 크게 자리잡고 있습니다. 하지만 아직 한국 게임이 가야할 길은 머나멉니다. 시간이 흐를수록 게임을 대체할 즐길 거리는 더더욱 늘어납니다. 제가 쓴 글이 허무맹랑한 이상론이거나 지나친 오바일 수도 있겠지만 좋은 일을 해서 스스로의 위상을 높인다는 생각이 쓸데없는 행위라고 생각하지는 않습니다. 부디 높은 자리에 계시거나 높은 자리에 가실 예정인 분들은 팔리는 게임 이전에 재밌는 게임과 끝내주는 게임, 그리고 사용자를 생각하는 게임에 대해서 한 번 생각해 주셨으면 합니다.



반응형
,
Posted by 알 수 없는 사용자
* 2013-07-05 책으로 출판하는 문제 때문에 내용의 일부를 추가했습니다. 책에는 원래 있던 내용은 제거되고 추가된 내용만 나갈겁니다.

꼼수의 앞부분을 보시려면 여길 봐 주시와~요: 
http://gamedevforever.com/84 

8. 비밀 정보는 쓰기 직전에 생성하고, 쓴 직후에 완전히 삭제하라

완전히 삭제한다는 말의 의미는, 그냥 free 하는 걸로 만족하지 말고, 먼저 0이나 다른 쓰레기 값을 채워넣은 다음에 함수에서 리턴 하거나, free 하라는 말이다. 그렇게 하지 않으면 엉뚱한 곳에서 중요한 데이터가 공격자에게 노출 될 수 도 있다. 이 문제는 심지어 인터넷 뱅킹 프로그램을 작성하는 사람 조차 모르거나 실수하는 것 중 하나로, 이 문제 때문에 인터넷 뱅킹의 보안 문제가 신문에 보도된 적도 있다. 예를 들어 다음과 같은 시나리오를 생각해 보자:

1. 사용자의 아이디와 패스워드를 암호화 해서 전송하기 위한 클래스의 인스턴스 X 를 생성한다.
2. 사용자의 아이디와 패스워드를 스크린 터치 방식을 이용해 입력 받는다.
   (키보드로 부터 입력 받는게 아니니 키보드 후킹에 안전하다! 오 안전해!)
3. 로그인 인증을 위해 입력받은 아이디와 패스워드를 1. 에서 생성한 인스턴스 X 에게 넘긴다.
4. 인스턴스 X 는 구글신도 깰 수 없는 조낸 강력한 암호화 기법으로 암호화 해서 서버에 보낸다.
   (현실세계에 존재하지 않는 조낸 안전한 암호화 방식이다. 안전하다! 오 안전해!)
5. 서버가 로그인 시도에 대한 결과를 넘겨 줬다. 결과는 4. 에서 사용했던 안전한 암호화 기법을 사용했기 때문에 역시 안전하다.
6. 공격자가 서버의 패킷이 클라이언트에게 가기 전에 가로챈 다음 클라이언트가 크래시 될만한 커다랗고 이상한 데이터를 대신 보냈다.
7. 서버가 보낸 정보 - 사실은 공격자가 보낸 데이터를 해석하지 못한 클라이언트는 크래시 됐다.

일단, 공격자는 클라이언트를 자신의 의도에 맞게 크래시 시켰다. 그러면, 공격자는 생성된 덤프 파일에서 무엇을 얻을 수 있을까? 먼저, 공격자는 스택 영역을 뒤져서 아이디와 패스워드 및 메모리의 어디쯤 사용자의 아이디와 패스워드가 저장되는지를 얻어 낼 수 도 있을 것 이다. 덤프 파일에는 프로그램이 크래시 되는 순간의 모든 정보가 저장되어 있으므로, 사용자의 아이디와 패스워드가 로컬 변수에 저장되어 있었다면 공격자는 필요한 모든 정보를 얻을 수 있을 것 이다. 나아가서 다른 사용자가 아이디와 패스워드를 입력했을 때 해당 정보가 저장될 메모리 위치도 얻었다. 어떻게?  보통 같은 과정을 거쳐 아이디와 패스워드가 입력되는 함수가 호출 되기 때문에 다른 사용자도 내가 입력한 아이디와 패스워드가 저장된 위치에 저장될 확률이 아주 높기 때문이다.

둘째로, 공격자는 아이디와 패스워드를 암호화 하여 전송하는 클래스의 인스턴스 역시 얻을 수 있다. 이쪽은 피해자의 아이디와 패스워드 뿐 아니라 통신에 사용되는 키도 얻어 낼 가능성이 있다. 공개키 방식의 통신 이라면 피해자의 개인 키 역시 알아낼 가능성도 있다. 아이디와 패스워드를 입력하고 확인하는 절차는 프로그램이 실행 된 뒤 시간이 많이 흐르지 않은 상태이기 때문에 암호통신 객체에 할당된 메모리는 항상 같은 위치일 확률이 올라간다. 그러므로 앞서 로컬 변수에 저장된 아이디와 패스워드를 얻어내듯 객체내에 저장된 데이터를 얻어낼 수 도 있게 된다.
 
위와 같은 이유들 때문에, 중요한 정보들은 사용하기 직전에 생성하고[각주:1] 사용한 직후에 메모리를 그냥 free하는 것이 아니라 0이나 다른 쓰레기 값으로 채워 넣어서 free 해줄 필요가 있는 것이다. 위의 시나리오를 예를 들자면 3과 4 사이에서, 2에서 입력받은 로그인 아이디와 패스워드가 저장되어 있는 로컬 변수에 0을 채워 넣는다. 그렇게 하면 스택에 저장되어 있는 정보가 제거된다. 또한, 4와 5 사이에서 3에서 넘겨받은 아이디와 패스워드를 저장하는 변수에 0을 채워넣어 정보를 제거해준다. 이렇게 데이터가 저장되어 있는 변수를 0이나 다른 쓰레기 값으로 채워 넣음으로서 공격자가 쉽게 피해자의 정보를 얻어내는 것을 차단할 수 있다.  

9. 임시 파일을 만들 때에는 임시파일 생성하는 함수를 이용하라

임시 파일을 생성해서 작업을 하는 경우, 작업의 내용을 공격자가 보거나 수정해도 상관 없는 경우엔 임시 파일의 이름이 어떻든 상관 없겠지만, 그렇지 않은 경우에는 매번 다른 파일 이름을 선택해 주는 것이 공격자를 조금 더 귀찮게 만들 수 있다[각주:2].


이를 위해서 UNIX* 계열에서 제공하는 함수로는 mkstemp() 라는 함수가 있다. 이 함수는 이전에 사용되던 tmpfile() 함수의 보안취약점을 제거한 버전으로 일단 생성에 성공했다면 유니크한 파일임을 보증해주고, 생성 프로세스 외에는 파일을 열 수 없도록 파일을 생성하자 마자 삭제해 버림[각주:3]으로서 레이스 컨디션을 이용한 공격법을 사용하지 못하도록 해준다. 당연히 생성되는 파일을 변조 하거나, 원하는 정보를 끼워넣는 등의 조작을 하기 위해서도 사용될 수 있다.

mkstemp() 함수가 아닌 다른 방법을 사용하는 경우, 공격자는 대충 다음과 같은 시나리오를 이용해 공격 할 수 있겠다. 시나리오에서 Victor는 피해자, Mallory는 악의를 가진 공격자가 되겠다.

1. Victor가 임시 파일을 만든다
2. Victor가 임시 파일에 내용을 써 넣는다.
3. 프로세스에게 할당된 시간이 다 지나가서 프로세스 전환이 이루어 진다.
4. Mallory가 미리 예측한 임시 파일의 이름을 open 해본다.
5. 오! Victor의 파일을 여는 것에 성공 했다!
6. Mallory는 유유히 Victor의 파일의 내용을 읽어 들이거나, 다른 값을 채워 넣는다. 쓰레기 값을 넣을 수 도 있다.
7. 할일 다 한 Mallory는 슬립해서 프로세스 전환을 유도한다.
8. Victor는 Mallory가 건드려 놓은 임시파일을 마음껏 사랑해 준다.
9. 파일을 닫는다.
10. 뭔가 사단이 난다. Mallory는 사단이 난 결과물을 가지고 즐거워 하며 뭔가 더 나쁜 짓을 시작한다.

물론, 2를 하기 전에 파일에 락을 거는 방법도 있다. 락을 걸어 버리면 조금 더 귀찮아 지긴 하겠지만 레이스 컨디션으로 공격하는 대상이 1. 과 2.의 사이나 8. 과 9. 의 사이가 될 수 있을 것이다. 레이스 컨디션을 이용한 공격 방법에 대한 자세한 설명은 말미에 있는 안랩에서 제공한 문서를 참조 하길 바란다.


MS 윈도우의 경우에는 mkstemp() 류의 함수는 존재하지 않는 것 으로 알고 있다. 최소한 구글님께서 2012년 2월 22일 글을 쓰고 있는 현재 알려주시지 않는 걸 보면 널리 알려져 있는 함수는 아닐 것으로 믿어마지 않는다. 대신에 ISO C++ 표준에서 제공하는 _mktemp_s() 함수를 사용하라고 권장하고 있다.

아래의 프로그램 코드는 윈도우에서 제공하는 함수들로 mkstemp() 와 유사한 동작을 하도록 만들어진 코드이다. FAT 이나 NTFS 자체가 POSIX 의 파일 시스템 과는 구조가 다르기 때문에 POSIX 에서 제공하는 안전성을 100% 보장한다고 볼 수 는 없다. 그러나 다른, 윈도우만의 방식으로 가장 안전한 임시 파일을 만들도록 작성된 코드 이므로 참고하거나 이용하는 것도 도움이 될 것 이다.

#include <windows.h>
   
static LPTSTR lpszFilenameCharacters = TEXT("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
   
static BOOL MakeTempFilename(LPTSTR lpszBuffer, DWORD dwBuffer) {
  int   i;
  DWORD dwCharacterRange, dwTempPathLength;
  TCHAR cCharacter;
   
  dwTempPathLength = GetTempPath(dwBuffer, lpszBuffer);
  if (!dwTempPathLength) return FALSE;
  if (++dwTempPathLength > dwBuffer || dwBuffer - dwTempPathLength < 12) {
    SetLastError(ERROR_INSUFFICIENT_BUFFER);
    return FALSE;
  }
dwCharacterRange = lstrlen(lpszFilenameCharacters) - 1; for (i = 0; i < 8; i++) { cCharacter = lpszFilenameCharacters[spc_rand_range(0, dwCharacterRange)]; lpszBuffer[dwTempPathLength++ - 1] = cCharacter; } lpszBuffer[dwTempPathLength++ - 1] = '.'; lpszBuffer[dwTempPathLength++ - 1] = 'T'; lpszBuffer[dwTempPathLength++ - 1] = 'M'; lpszBuffer[dwTempPathLength++ - 1] = 'P'; lpszBuffer[dwTempPathLength++ - 1] = 0; return TRUE; } HANDLE SpcMakeTempFile(LPTSTR lpszBuffer, DWORD dwBuffer) { HANDLE hFile; do { if (!MakeTempFilename(lpszBuffer, dwBuffer)) { hFile = INVALID_HANDLE_VALUE; break; } hFile = CreateFile(lpszBuffer, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, 0); if (hFile == INVALID_HANDLE_VALUE && GetLastError( ) != ERROR_ALREADY_EXISTS) break; } while (hFile == INVALID_HANDLE_VALUE); return hFile; }

맹글기 귀찮아서 'Secure Programming Cookbook for C and C++'에 있는 소스를 그냥 썼는데... 안될까요???
직접 맹글어야 하나요??? 우리만 보고 비밀로 해줘요

Note. FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE 플래그를 사용하면, 생성되는 파일은 메모리가 부족하지 않은 한 하드 디스크로 플러시 되지 않으며, 파일을 닫는 순간 삭제가 된다. 그러므로, 공격자는 파일의 내용을 볼 수 없게 된다.

위의 프로그램을 보면, 일반적인 rand() 함수가 아닌 spc_rand_range() 라는 함수를 이용해서 난수값을 만드는 것을 볼 수 있다. rand() 함수는 시드가 없는 경우엔 항상 동일한 숫자열을 생성해 내고, 시드를 이용해 초기화 해도 시간을 많이 사용하기 때문에 비교적 예측하기가 쉬운 편이다. 그래서 예측이 더 어려운 난수 생성 함수가 필요한 경우에 사용되는 함수가 spc_rand_range() 함수이다.

[각주:4]혹여나 그래도 필자처럼 POSIX 표준이 아니면 죽음을 달라(응?)라는 고집을 가진 프로그래머가 있다면, 아래의 코드를 사용하면 될 것이다. 아래의 코드는 wcecompat(https://github.com/mauricek/wcecompat/)이라는 프로젝트의 코드 일부분을 발췌해 낸 것으로 mkstemp() 함수의 구현 코드이다.

/* mkstemp extracted from libc/sysdeps/posix/tempname.c.  Copyright
   (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.  */

static const char letters[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

/* Generate a temporary file name based on TMPL.  TMPL must match the
   rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
   does not exist at the time of the call to mkstemp.  TMPL is
   overwritten with the result.  */
int
mkstemp (char *tmpl)
{
  int len;
  char *XXXXXX;
  static unsigned long long value;
  unsigned long long random_time_bits;
  unsigned int count;
  int fd = -1;
  int save_errno = errno;

  /* A lower bound on the number of temporary files to attempt to
     generate.  The maximum total number of temporary file names that
     can exist for a given template is 62**6.  It should never be
     necessary to try all these combinations.  Instead if a reasonable
     number of names is tried (we define reasonable as 62**3) fail to
     give the system administrator the chance to remove the problems.  */
#define ATTEMPTS_MIN (62 * 62 * 62)

  /* The number of times to attempt to generate a temporary file.  To
     conform to POSIX, this must be no smaller than TMP_MAX.  */
#if ATTEMPTS_MIN < TMP_MAX
  unsigned int attempts = TMP_MAX;
#else
  unsigned int attempts = ATTEMPTS_MIN;
#endif

  len = strlen (tmpl);
  if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
    {
      errno = EINVAL;
      return -1;
    }

/* This is where the Xs start.  */
  XXXXXX = &tmpl[len - 6];

  /* Get some more or less random data.  */
  {
    SYSTEMTIME      stNow;
    FILETIME ftNow;

    // get system time
    GetSystemTime(&stNow);
    stNow.wMilliseconds = 500;
    if (!SystemTimeToFileTime(&stNow, &ftNow))
    {
        errno = -1;
        return -1;
    }

    random_time_bits = (((unsigned long long)ftNow.dwHighDateTime << 32)
                        | (unsigned long long)ftNow.dwLowDateTime);
  }
  value += random_time_bits ^ (unsigned long long)GetCurrentThreadId ();

  for (count = 0; count < attempts; value += 7777, ++count)
    {
      unsigned long long v = value;

      /* Fill in the random bits.  */
      XXXXXX[0] = letters[v % 62];
      v /= 62;
      XXXXXX[1] = letters[v % 62];
      v /= 62;
      XXXXXX[2] = letters[v % 62];
      v /= 62;
      XXXXXX[3] = letters[v % 62];
      v /= 62;
      XXXXXX[4] = letters[v % 62];
      v /= 62;
      XXXXXX[5] = letters[v % 62];

      fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE);
      if (fd >= 0)
    {
      errno = save_errno;
      return fd;
    }
      else if (errno != EEXIST)
    return -1;
    }

  /* We got out of the loop because we ran out of combinations to try.  */
  errno = EEXIST;
  return -1;
}
 
10. try ... catch 를 사용 하지 않는 것은 '공격할 수 있으면 해봐' 라고 도발하는 것과 같다.
 

윈도우 프로그램이 문제가 발생되어 크래시 되면 닥터 왓슨[각주:5]께서 코어를 덤프해 주신다. 그리고 고마우시게도 프로그래머가 고생하지 말라고 덤프된 코어에는 프로그램에서 다루고 있는 정보들이 가득 들어있다. 한 바이트 한 바이트 정성스레 모은 메모리 내용이 한 비트도 안 빠지고 예쁘게 코어덤프 파일에 켜켜히 쌓여있다. 그렇기 때문에 코어덤프를 유발하는 것도 훌륭한 공격이 된다.  공격자가 필요한 순간에 크래시를 일으킬 방법만 찾는다면 메모리를 액세스 하지 못하도록 보호 하는 것 자체가 의미 없어질 수 도 있다.

공격자가 원하는 시점에서 프로그램을 크래시 시키는 방법은 꽤 여러가지가 있다. 어떤 방법을 사용하건 메모리를 살짝 건드려 주기만 하면 되기 때문에 크래시를 일으키는건 수퍼 유저의 권한을 얻거나 바이러스를 심는 것 보다 훨씬 쉽다. 어떤 방법을 쓰던 크래시만 일으켜 주면 닥터 왓슨께서 혹은 그 후계자 께서 해킹하기 편하라고 메모리의 모든 데이터를 모아서 파일로 만들어 주시니 얼마나 좋은가? 어떤 공격법을 사용할지, 어떻게 사용할지만 창의력을 발휘하면 된다. 

이정도 창의력 이라면 충분하다!

신중하게 범위 체크 하고, 값을 밸리데이션 하고, 자원을 액세스 할 때 마다 검증을 하면 궂이 try ... catch 구문을 사용하지 않아도 될 수 있다. 그러나 인간은 실수를 저지를 것을 전제로 창조된 존재다, 실수 하지 않는다면 그건 창조주에 대한 배신이다. 자신이 절대로 실수 하지 않을 것 이라고 믿는 것은 죄악이다! 그러니 절대로 이룰 수 없는 무결점 프로그램을 만들기 위해 150%의 노력을 들이느니, try ... catch 구문에게 적당히 떠넘기고 85%의 노력만 기울이는 쪽을 추천한다. 

11. 항상 메모리 범위 체크를 하라

스택 오버플로우, 힙 오버 플로우 합쳐서 버퍼 오버플로우! (응? 뭔소리냐?) 스택 오버플로우와 힙 오버 플로우는 공격의 대상이 다르고 그 효과가 다르긴 하지만 기본적으로 버퍼 오버플로우 공격에 해당된다. 버퍼 오버플로우 공격을 한마디로 줄인다면 '데이터 홍수' 되겠다. 준비한건 100바이트 뿐인데 150바이트가 들어왔다면 메모리 구조의 특성상 연달아 있는 다른 데이터 영역을 침범하게 되는데, 그 침범당하는 영역이 무엇이냐에 따라 공격의 내용이 바뀌게 된다.

준비된 메모리 영역보다 들어온 데이터가 더 많다면 요로케 되는 거시다! (수해를 입으신 분들껜 애도를)

힙 오버플로우 공격은, *alloc() 계열의 함수로 할당된 힙 영역에 존재하는 데이터 버퍼에 허용이상의 데이터를 밀어 넣음으로서 연달아서 할당받은 다른 데이터 영역의 데이터를 수정하는 방법이다. 이렇게 수정함으로서 포인터류 - 구조체의 인스턴스, 클래스의 인스턴스, 메소드, 링크드 리스트 등등등의 포인터를 수정하거나, 데이터 자체를 수정하는 것을 목적으로 한다. 그러나, 힙의 특성상 활용하기 위해서 더 많은 제약이 필요하기 때문에 스택 오버플로우에 비해 상대적으로 덜 사용된다[각주:6].

스택 오버플로우 공격은 로컬 변수로 잡힌 배열을 대상으로 한다. 주로 char[] 배열이 그 공격 대상이 된다. 설명을 위해 함수가 호출되면 스택이 어떻게 동작하는지 잠시 
먼저 설명하겠다.

한글버전의 일러스트가 아닌 점에 대해서는 심심합니다(응?) 
 
그림의 연두색 부분이 DrawLine() 이라는 함수가 호출되었을 때 스택의 상태이다. 스택의 가장 아래쪽에는 DrawLine() 함수에게 넘겨지는 매개변수가 들어가고, 그 다음에는 리턴 주소가, 마지막으로 로컬 변수들이 할당된다.

그림에서 볼 수 있듯이, 로컬 변수가 돌아가야 되는 장소의 주소를 기록한 메모리의 윗쪽에 위치하기 때문에 지역변수가 배열인 경우 배열의 크기보다 더 많은 데이터를 입력해 주면 이 주소 영역까지 침범이 가능하고, 잘 이용하면 다른 코드를 실행할 수 도 있다.

이러한 공격에 저항하기 위해 i586 이후 부터는 코드 영역이 아닌 곳에서는 프로그램을 실행하지 못하도록 CPU에서 막아주는 기능을 제공하고는 있지만, 이를 위해서는 운영체제가 수정되어야 하는데, 아직 그 기능을 사용한 운영체제가 있다는 이야긴 듣지 못했다. 추가로, 인텔에서 생산하는 IA64[각주:7]의 경우에는 스택 오버플로우 공격을 막기위해 레이어드 스택이라는 개념을 도입하기도 했다.

우좌지당간에, 버퍼 오버플로우 공격은 상당히 강력한 공격법 이긴 하지만, 버퍼를 사용할 때 메모리 영역을 착실하게 확인만 해주면 아무런 문제가 발생하지 않는다. 그러므로, 어떤종류의 버퍼이든 관계없이
버퍼를 복사 할 때에는 항상 메모리 크기 확인하고, 
배열을 로컬변수로 사용할 땐 항상 배열의 크기와 입력 데이터의 길이를 비교 해야한다. 좀 더 실제적인 경우를 예로 든다면, 입력된 데이터를 복사할 때 문자열 복사라고 해서 strcpy()를 사용하면 안된다는 말이다. 특히 C/C++ 언어를 배울때 문자열을 다루는 함수는 str* 계열의 함수를 배우게 되는데, 이 함수가 쓰는 입장에서 편리할 뿐 아니라 공격자 입장에서도 아주 편리한 함수이다. 이런 함수들의 문제점은 아주 오래전부터 알려져 있음에도 불구하고, 여전히 C/C++을 가르칠 때엔 strn*() 계열이 아닌 str*() 계열을 가르치고 있다. 이유는? 아무도 모른다.

사용자가 적당한 길이로 값을 입력해 주리라 예측하고 프로그램을 만들어서는 안된다. 사용자의 아이디를 입력받기 위한 버퍼의 크기가 1000 바이트면 적당하다고 생각하는가? 1001자를 입력해 보고 버퍼 오버플로우를 일으키는 사용자- 공격자가 아닌 일반 사용자는 반드시 있다. 사용자의 패스워드가 100자면 충분하고도 남는다고 생각 하는가? 한때 255자 패스워드가 유행한 적도 있었다. 그 당시 255자 패스워드 한번 안 써본 사람은 제대로된 서버 관리자가 아니라는 소리도 했었다. 그 당시 필자가 관리하던 서버의 패스워드는

dughdhksmsskdmlahrwktlslsorpqnwhrgkadldjqtdmflfhekrmrkskfmfvnfmschwkddpsndltlautnlfaksgksanfrkdmfhdlsehgktlsmsehek


였었다. 불행하게도150자도 안되어 다른 관리자들이 웃고 떠드는 와중에 끽소리 못했었다.

우좌지당간에, 사용자들은 전부 창의력 대장들이다. 프로그래머의 의도대로 프로그램을 사용해주는 사용자는 그다지 많지 않다. 아니, 프로그램을 만든 자신 조차도 만들고 나서 몇주 지나면 자신이 처음에 만든 의도와 다르게 프로그램을 사용하는건 당연하다. 그러므로 버퍼를 다루는 모든 코드에는 항상 버퍼의 크기와 입력된 - 키보드에서 입력 되었든, 네트워크를 통해서 전달 받은 데이터든 상관없이 - 데이터를 비교해서 데이터가 버퍼에 충분히 들어갈 수 있는지 확인하고, 그렇지 않다면 뱉어내는 코드를 잊지 말고 작성해 주어야 한다.

마찬가지로 memcpy 등을 이용해서 메모리 버퍼를 복사 할 때에도 복사해 주는 쪽의 버퍼 크기를 기준으로 복사하는 것이 아니라, 복사 받는 쪽의 버퍼 크기를 기준으로 복사 해야 한다. 데이터의 검증이 완료 되기 전에는 10초전에 같은 프로그램이 저장해 두었던 파일을 다시 열어서 처리하는 경우에도 데이터가 완전할 것 이라고 믿어선 안된다.

주저리주저리 설명을 했지만, 한마디로 줄여서 다시 말한다면 '데이터를 버퍼에 다 담을 수 있는지 항상 확인해라' 이다.


12. 값의 밸리데이션은 옵션이 아니다.

많은 프로그래머들이 입력된 값이 요구한 값과 같을 것 이라는 잘못된 전제를 가지고 프로그램 코드를 작성한다. 그러나, 사용자들은 절대 '알파벳만 입력해 주세요'라고 메시지를 띄웠다고 해서 알파벳만 입력해 주지는 않는다. '한글도 알파벳' 이라고 생각하는 사용자 부터, '다른 문자를 넣으면 어떻게 될까'라는 단순한 호기심을 가진 사용자, 그리고 공격할 틈을 찾는 크래커 등등 잘못된 입력은 항상 이루어지기 마련이다.

네트워크를 통해 전달된 데이터도 마찬가지 이다. 클라이언트에서 체크해서 보내는 데이터 이기 때문에 서버는 아무런 신경을 쓰지 않아도 된다고 생각한다면, 그건 경기도 오산에서나 있을 일이다. (어디서 쌍팔년도 개그를 날리고 XX이얏!) 마찬가지로, 서버에서 보내주는 데이터 이기 때문에 안전하다고 생각하면 안된다. 공격자들은 개구멍 찾는데 있어서는 하늘도 놀랄 창의력을 가지고 있다. 

밸리데이션을 할 때에는 다음과 같은 전제 조건을 생각하라

ㄱ. 확인하기 전엔 모든 입력값 - 키보드로 부터의 입력이든 네트워크를 통해 전송된 것이든 상관없이 프로그램의 외부에서 전달된 데이터는 모두 공격자의 농간이 있을 것 이라고 생각하라. 게임이 릴리즈 되고 인기를 끌게 되면 어떻게든 정당하지 않은 방법으로 이득을 얻어보려는 찌질이 들이 등장하게 될 것이다. 일단 그런 찌질이 들이 등장하면, 축하한다. (응???) 그리고, 안전하다고 여겼던 바로 그 방법을 통해 클라이언트를 공격하려고 시도할 것 이다. 그러니 확인되지 않은 모든 입력값은 전부 해커가 공격하려고 시도한 값이다.
ㄴ. 밸리데션은 입력시와 인스턴스 내에서 사용하기 전 모두에서 확인하여야 한다. 입력되는 데이터가 올바른 것인지 여부는 그 형태가 올바른지를 확인하는 정도를 넘기가 쉽지 않다. 공격자가 입력 밸리데이션을 통과할 수 있도록 패턴을 맞추고, 실제 처리시에 문제를 일으킬 만한 데이터를 고안해 낼 가능성이 있다. 예를 들자면 이메일을 입력하는 경우에, 이메일의 패턴에는 맞지만 실제로는 이메일 정보가 아닌 데이터가 입력될 수 도 있다. 그렇기 때문에 그 데이터가 실제로 사용되기 직전에 다시한번 프로그램의 동작에 부합하는 데이터 인지를 다시 한 번 확인해야 한다.

위의 두 베이스 가이드는 게임 클라이언트를 대상으로 했기에 일반적인 클라이언트나 서버 프로그램을 작성해야 하는 경우에 감안해야 하는 다른 내용들은 설명하지 않았다. 다음은 몇몇 실질적인 가이드 이다.

ㄱ. 액세스 하는 파일의 패스에 대한 밸리데이션을 반드시 하라

내가 입력한 패스 값을 쓰는데 뭐가 문제? 라고 생각하면 Orz. 상대 패스를 이용하여 파일을 액세스 하는 경우에는 '../' 몇개로 가능한 공격들도 있다. 물론 앞에서 말한대로 파일을 액세스 할 때 무조건 절대 패스를 사용할 수 있다면 문제가 되지 않는다. 그러나 모든 파일을 절대 패스를 이용하여 액세스 하는 데에는 제한이 있을 수 밖에 없고, 상대 패스를 이용하여 파일을 액세스 해야 하는 경우라면 액세스 하려는 파일이 내가 의도한 것이 맞는지를 확인해 줄 필요가 있다. 지정된 상대패스를 절대 패스로 바꾸는 함수들은 다음과 같은 것 들이 있다:

realpath() in C
getCanonicalPath() in Java
GetFullPath() in ASP.NET
GetFullPathName() in C++

위 언어 이외의 다른 언어라면 틀림없이 유사한 함수를 제공 할 터이니 확인해 보고, 파일을 액세스 할 때에는 반드시 의도한 파일이 맞는지 여부를 확인하는 것을 잊지 말라. 

ㄴ. *printf() 계열의 포멧 스트링을 사용하는 경우엔 주의하라
 

특히 입력된 데이터를 전송하거나, 전송 받은 데이터를 포멧 스트링에 넣을 때에는 주의하라. 포멧을 지정하는 함수를 사용하는 경우, 공격자가 입력에 사용되는 문자열에 어떤 문자를 끼워 넣을지 알 수 없다. 특히 %s나 %n을 사용할땐 외부에서 입력되는 데이터를 검증없이 사용해서는 안된다. 입력받은 데이터를 포멧 스트링에 집어넣는 경우에는 아래에서 설명할 Neutralization을 시행하는 편이 더 안전 할 것 이다.

일반적으로 *sprintf() 계열의 함수를 사용할 텐데, 위에서 설명했던 것과 동일한 이유로 vsnprintf(), snprintf(), vasprintf() 그리고 asprintf() 함수를 사용하여 출력하는 데이터의 포멧을 지정하는 편이 유사한 동작을 하는 다른 함수에 비해 안전하다. 특히 %s를 사용할 때엔 가능하다면 그냥 %s로 사용하지 말고 %.10s와 같이 최대폭을 고정해 사용하는 편이 좋다.

마지막으로, CString 클래스의 Format() 메소드 역시 sprintf() 함수와 유사한 문제점이 있는 것으로 알려져 있다. 그러므로 CString 클래스를 사용하는 경우 MS에서 뭔가 해주었을 것 이라고 믿는 것 보다는 스스로 주의 하는 편을 권한다.
 

ㄷ. 입력받은 데이터를 서버로 보낼땐 항상 Neutralization을 하라

흔한 공격법 중에 쿼리 인젝션과 커멘드 인젝션이 있다. 이름은 다르지만, 성이 같은 걸 봐서는 같은 집안 녀석들 이라는 걸 알 수 있을 것 이다. 이 두놈은 성에서 알 수 있듯이, 입력에 무언가를 끼워 넣어 하는 공격이다. 특히 쿼리 인젝션은 에지간한 웹 프로그래머라는 다 알고 있을만큼 유명한 공격법이다. C/S 환경역시 웹 환경과 다르바가 없기 때문에 좀 말이 안되는 단순한 공격 예를 보이도록 하겠다.

"SELECT * FROM TBL_USERS WHERE USER_NAME='" + userName + "' AND PASSWORD='" + userPass + "'";

위와같이 전달된 문자열을 이용해서 쿼리를 생성, 실행하는 코드가 있다고 가정하자. 사용자가 아래와 같이 아이디를 입력하는 경우, Neutralization이 시행되지 않는다면, 공격자는 패스워드를 몰라도 인증을 통과할 수 있다.

bamboo' --

위와같이 유저 아이디를 입력하고 전송하면 위의 쿼리는 아래와 같이 만들어진다.

SELECT FROM TBL_USERS WHERE USER_NAME='bamboo' -- ' AND PASSWORD='something'

-- 가 주석을 의미하므로, 프로그램은 bamboo라는 사용자가 아이디와 패스워드를 제대로 입력했다고 인식할 확률이 높다.

물론 위와 같은 공격에 성공하려면 시간을 필요로 하고 쿼리를 정확하게 아는 사람이 아닌 다음에는 공격에 성공할 것 이라는 확신을 가질 수 도 없다. 게다가 쿼리가 조금만 더 복잡해진다면 위의 공격 방법은 확실하게 실패 할 것 이다. 그러나, 여전히 공격에 성공할 확률을 0%라고 안심 할 수 도 없다.

위와같은 공격 방법을 막는 길은 상당히 많다. 그러나 가장 널리 사용되는 방법은 Neutralization 이라는 방법으로, 간단하게 말하자면 위와 같은 상황에서 공격에 이용할 만한 가능성이 있는 문자를 다른 문자로 대체 하는 것이다. 가장 대표적인 Neutralization 함수는 PHP에서 사용되는 mysql_real_escape_string()‎함수가 있다. 물론 쿼리 인젝션을 막는 다른 방법들도 많다. Neutralization은 일반적인 경우에 쉽게 사용될 수 있는 간단한 방법이자 예상하지 못한 공격을 사전에 차단할 수 있는 방법이기도 하다. 

*     *     * 

다음번엔 포프님이 좋아할 지도 수도 있겠다는 생각을 할만한 내용이라고 판단할 근거가 있다고 볼만한 이야기를 해보겠다. 처음에 이야기 할 때 절대로 막을 수 없다던 사회 공학적인 해킹공격에 대처... 는 안되겠지만, 만약의 경우에 회사를 보호하고 안되면 자기 자신만 이라도 보호 하는 방법에 대해 다루어 보겠다.

* 참고 사이트

CWE: Common Weakness Enumeration:
Race condition을 이용한 공격: Ahn Lab.
* 참고자료
Secure Programming Cookbook for C and C++, O'Reilly, 2003


  1. 입력을 받는 것을 포함하여, 복호화 하거나 인증서에서 키 값을 뽑아내는 등 필요한 정보를 메모리에 로드하는 모든 동작을 말합니다. [본문으로]
  2. 사실, 임시파일의 내용을 봐도 상관 없는 경우라 해도 에러 처리를 제대로 해주지 않으면 아주 훌륭한 해킹의 대상이 될 수 있습니다. 이 내용에 대해서는 아래에서 설명 하도록 하겠습니다. [본문으로]
  3. UNIX* 에서 사용되는 대부분의 파일 시스템은, 파일이 rm 명령 등으로 삭제가 되어도 파일의 이름과 해당 파일의 데이터 시작 위치만 삭제될 뿐 실제로 데이터가 제거 되지는 않습니다. 또한, 해당 파일을 열고 있는 프로세스가 있는 동안에는 파일이 삭제되었다 해도 다른 프로세스가 해당 파일이 사용하고 있는 데이터 영역을 할당 받지 못하도록 되어있습니다. 그렇기 때문에 열려있는 파일을 삭제 하더라도, open() 함수 등으로 해당 파일을 열 수 없을 뿐, 현재 열려있는 파일 디스크립터로 파일을 액세스 하는 것은 보장이 됩니다. [본문으로]
  4. 이 내용은 책으로 내면서 위의 코드가 저작권과 관련되어 귀찮은 상황을 만들어 낼거 같아 새로 확인한 내용입니다. 책에는 'Secure Programming Cookbook for C and C++' 에서 발췌한 코드는 못 들어가겠지요. 의도하지 않았지만 책과 웹 페이지의 차별점이 되어 버렸네요. [본문으로]
  5. Windows 7과 2008의 경우에는 Dr. Watson은 은퇴 하셨지만, 대신에 *.dmp 파일을 만들어 주는 서비스가 움직이고 있습니다. 방어자의 입장에서 더더욱 불행한 것은, 작업관리자에서 특정 프로세스의 *.dmp 파일을 만들어 준다는 것 입니다. 메모리 액세스를 막아주는 프로그램이 이 기능도 막을 수 있는지는 확인해봐야 하겠지만, 운영체제에서 제공해주는 기능이니 아마 안되지 싶네요... [본문으로]
  6. Arm 계열이나 다른 마이크로 프로세서의 경우에는 메모리가 충분하지 않고, 힙과 스택의 충돌이 발생할 수 있기 때문에, 힙 오버 플로우 공격으로 스택영역의 데이터를 건드리는 공격을 할 수 도 있습니다. Arm을 사용하는 가장 대표적인 게임기가 바로 닌텐도 입니다. [본문으로]
  7. 인텔에서 발표한 64비트 RISC 칩 입니다. i586 계열과는 호환성이 없는 CPU로 이것 때문에 AMD한테 추월 당할 뻔 했었지요 - 실제로 추월 당했다고 생각하시는 분도 있는듯 합니다. 인텔에서 이거 만들고 있는 동안 우리가 쓰고 있는 64비트 CPU를 먼저 설계한건 AMD였습니다. 그래서 자료를 찾아보면 인텔측 것 보다는 AMD측의 것이 더 많고 쉽게 찾아지지요. [본문으로]
반응형
,