저번 까지는 화면에 우주선을 움직이게 까지 했다.
이번에는 게임의 하이라이트 충돌이다.

물체간에 부딛히는 것을 인식하게 하는 것이다.
여기에는 실제로 많은 물리 지식과 수학적 지식이 필요하다.
나도 책을 여러번 읽고 이해를 할 수 있었다.
여기에 충돌에 관한 물리적 내용을 다루지는 않을 것이다.
실제 자세한 구현과 코드의 내용을 보려면 직접 책을 참고하는 것이 좋을 것이다!
(2d 게임 프로그래밍, 찰스 켈리)

이 글의 목적은 게임 상의 우주선이 게임 화면 안에서 갇혀있는 것이다.
우주선이 왔다리 갔다리 하는데 화면 끝으로 이동했을 때 넘어가는 것이 아니라
공이 벽에 튕기듯이 튕겨서 게임 화면 안에서 계속 돌아댕기는 프로그램을 만들 것이다.

충돌을 다루기 위해 벡터를 사용해야한다.
DirectX에서 제공하는 벡터 타입이다.
벡터 연산들도 제공한다.

그러나 이름이 길고 쓰기 불편하므로 우리가 이해하기 쉽게 간단한 이름으로

캡슐화 시켰다.


(벡터 재정의)



(벡터 연산)


위와 같이 벡터 타입과, 벡터 연산들을 우리가 사용하기 편하게 함수안에 넣어두었다.

그 다음 충돌을 다루게 하기 위해 Entity 라는 이름의 클래스를 만들어 볼 것이다.
Entity는 해석하면 객체이지만, 여기서는 고유명사처럼 Entity라고 말할 것이다.

Entity는 우리가 충돌을 인식하게 하기 위해 우리가 원하는 객체들을 Entity 클래스로 선언할 것이다.

그리고 각종 충돌에 관한 함수들을 집어 넣을 것이다.



(entity 이름 영역)


Entity의 이름 영역에는 충돌타입과 중력상수가 적혀있다.
충돌 타입은
원형 충돌
사각형 충돌
회전 사각형 충돌
복합 충돌
으로 구분할 수 있다.

그러한 것들을 이름영역에 정의해두었다.

다음은 Entity 클래스의 속성 변수들을 볼 것이다.



(속성 변수)


가지고 있는 속성 변수에는 위와 같다.

그리고 protected 영역에 선언된 함수들이다.



(protected 함수)


위 함수들은 다른 클래스 밖에서 호출되지 않고 내부에서만 호출되게 하기 위해 protected 안에서 선언되어있다.

이제 public 영역의 함수들을 볼 것이다.



(생성자)


생성자이다.
초기값들을 설정하는 부분이다.

다음으로는 초기화 함수이다.



(초기화 함수)


초기화 함수가 조금 특이하다.
첫번째 인자가 게임포인터라는 것만 빼면 Image의 초기화 함수랑 똑같다.
심지어 내부에서 Image의 초기화 함수를 호출한다.

게임 포인터에서 입력을 input으로 저장해둔다.
게임 중 입력 받아놓은 지역변수 값들을 가져와서 저장해두는 역할을 한다.

(activate() & update())


activate() 함수는 Entity 객체가 충돌관련 연산이 일어나게 할지 활성화 여부를 활성화 시키는 것이다.

update 함수는 계산된 속도 변화를 적용시킨다.
이전에 충돌과 관련된 계산이 끝난 후 변화 속도를 deltaV에 저장해 두고 여기서 적용시킨다.

그 후 Image의 update를 호출한다.


(ai 함수)


AI 함수는 인공지능에 관련된 함수이다.
지금 현재 우리의 객체에는 인공지능이 들어가지 않는다.
그렇지만 Entity 클래스의 객체를 인스턴스화 할 수 있게 하기 위해 빈 함수로 제공해둔다.

이렇게 하면 Entity 클래스에서 객체를 직접 생성할 수 있다.



(충돌 함수)


충돌에 관련된 함수이다.
위 함수는 각 객체의 충돌 타입을 확인 한 후 각 객체의 충돌 타입에 맞게 다시 충돌탐지 함수들을 호출한다.
각 충돌 탐지에 관한 함수들, 내용들은
시간이 된다면 따로 정리해서 올릴 예정이다.

그 후 이 전까지와 달라진 점이 있다.
이젠 Entity를 사용해 각 객체를 호출 할 것이다.
그러기 전에 행성과 우주선이라는 클래스를 따로 만들 것이다.
그리고 이 우주선과 행성 클래스는 Entity 클래스를 상속한다.
우주선과 행성 클래스를 따로 만드는 이유는 각 객체의 값들을 따로 관리하는게 좋기 때문이다.
여기저기 섞여 있는 것보다는!

planet 부터 보자!


(planet 헤더)


헤더 파일에 planet 의 속성들이 들어있다.



(planet 생성자)


생성자는 간단히 이름영역에서 설정한 값들을
변수에 초기값으로 설정하는 역할을 한다.

다음은 ship 클래스이다.


(ship 헤더)


planet 과 같이 속성 설정값들이 있다. ( 이 값들은 애니메이션과 관련된 설정들도 있고 지난 글들에서 다루어왔던 값들이므로 설명은 생략하겠다. )

planet과 다른점은 update 함수가 있다.

이 update 함수는 우주선이 벽에 부딛히고 튕겨져 나오는 것을 작성하기 위해 여기서 상속받아서 작성하였다.


(ship의 생성자)


planet과 마찬가지로 ship의 생성자 또한 이름영역에서 설정된 값들을 가지고 초기값을 설정하며 만들어진다.



(update 함수)


update 함수이다.
이 함수에서는 맨 먼저 Entity 의 update가 실행된다. Entity의 update에서 충돌 결과의 움직임이 적용이 되고 Image의 update가 실행이 된 후이다. 마지막으로 화면에 그려지기 전에 이루어지는 update이다.

Entity의 update에서 적용된 속도 값들을 이용하여 해당 우주선의 화면상 위치를 잡는 모습이 보이고
추가적으로 우주선이 천천히 뱅글뱅글 돌아가도록 spriteData의 angle 값을 바꾸고 있는 모습도 보인다.

그 뒤로 이제 화면에 부딛히는 과정을 체크한 후 부딛히면 날라온 방향에서 부딛힌 축에 뒤집히게 -1을 곱하여 반대로 뒤집어준다.

이제 실제 spacewar 파일에서 우주선과 행성 등을 다시 선언해주어야한다.
이 전까지는 Image로 선언했기 때문이다.



(재선언)


이 때 우주선은 Ship으로 행성은 Planet으로 선언했다.
둘 모두 Entity를 상속했고 Entity이고 Entity 는 Image를 상속했다.

실행해보면 우주선이 게임화면에서 계속 튕기면서 돌아댕기는 모습을 볼 수 있다.



(우주선 표류)



(우주선 표류)


다음에는 두 객체가 충돌하는 것에 관련해서 알아볼 것이다.
아까 Entity에서 충돌에 관련해 만들어두었지만 아직 안썼었다.

(* 참조 - 2d 게임 프로그래밍, 찰스 켈리)

  오늘 분석한 자료는 overlapping chunks이다.
이 방법은 The Forgotten Chunks 라는 문서에서 가져왔다고 하여

해당 문서를 먼저 읽어보았다.



(참고 문서)


저 페이퍼는 정말 재밌었다.
작명센스도.! 잊혀진 청크..!

전략을 간략히 말하자면
heap overflow를 통하여 청크의 SIZE 필드를 조작한다.
그리하여 malloc 혹은 free를 통하여 중첩된 영역을 얻는다.

* 중첩된 영역을 얻는다 라는 것에 대해서
-> 예를들어 A라는 malloc 영역이 주소 4~8 이라고 하자.
그리고 B에 malloc을 받는데 0~11의 주소로 할당을 받는다고 한다면?
B 주소 영역에 A의 주소 영역이 포함되어있다. 그렇기에 B에 작성하므로 A영역의 데이터를 건들일 수 도 있고, A에 작성하므로써 B영역의 데이터를 건들일 수 도 있는 상황이 된다.

우리는 중첩된 영역을 얻는 것을 목표로 할 것이다.

먼저 3개의 malloc 주소를 할당받는다.


(malloc)


이 때 보면 malloc의 사이즈가 조금 의도된 듯한 느낌이 든다.
바로 -8 을 해준 이유는 사용 가능한 영역까지 꽉 할당받기 위해서이다.

포스트 글 malloc의 사용가능 영역
에 정리해 둔것을 참고하면 좋다.
https://blog.naver.com/rlagkstn1426/221212549437

실제의 상황에 위와같이 할당을 받고 문자열을 입력받는다면
쉽게 다음 chunk의 사이즈 영역을 건들일 수 있다.

그 후 각각의 heap 영역을 표시하기 위해 아래와 같은 작업을 한다.



(영역 표시)


p1 에는 1로 가득채우고 , p2는 2로, p3은 3으로

그리고 p2를 free해준다.



(free)


p2 를 free 해준다.

그렇게 되면 가운데가 구멍이 뚫린 상황이 된다.
p1 - p2(free됨) - p3

실제 같으면 p1에 heap overflow를 이용하여 p2의 사이즈 영역을 덮어썻을 것이지만
테스트 코드이므로 직접 사이즈 영역에 값을 작성할 것이다.

(아직 우리가 해당 주소를 가지고 있으므로 p2에!)



(적어줄 사이즈)


evil_chunk_size는 우리가 가짜로 적을 사이즈이고,
evil_region_size는 우리가 추후에 malloc을 할 때 사용할 값이다.

우리는 지금 free 된 p2의 영역의 SIZE 필드에 0x181이라고 적을 것이다.
맨 마지막 비트가 1인 이유는 이전 청크가 사용중이기 때문에 1이라고 섬세하게 맞춰준다.



(SIZE 조작)


그렇게 되면 p2의 영역은 free된 상황인데,
free 된 청크의 크기가 0x180 이라고 설정되어있는 상황이다.

변화 과정을 메모리에서 살펴보면


(덮어쓰기 전)



(덮어쓴 후)


덮어쓴 후 p2의 메모리 영역에 SIZE를 보면 0x181이라고 덮어쓰여진 것을 확인 할 수 있다.

그렇기에 지금 freelist에 p2가 있을 것이고,
p2의 청크 크기는 0x180으로 설정되어있는 셈이다.

그렇다면 여기서 malloc(0x180)을 한다면?



(malloc(0x180))


그러면 p2의 영역을 주는데 0x180 만큼의 크기를 할당해줄 것이다.
그러면 p3는?
-> 잊혀진 것이다. 알 길이 없다. (The Forgotten Chunks 의 작명센스!)

return 된 주소를 확인해보자.


(return 된 주소)



(return 영역)


return 된 영역을 보면 원래 p2의 시작점인데
사이즈는 0x180만큼으로 그 안에 p3 영역이 있는 것을 볼 수 있다.

덮어쓰여진 것을 한번 테스트해보자!



(4로 덮어쓰기)


p4안에 p3이 들어있다.

그러면 p4의 영역을 4로 가득채우면?



(결과)


p3의 영역 또한 4로 덮어쓰여지게 된다.

그러면 p3을 3으로 다시 덮어쓰면?



(3으로 덮어쓰기)



(결과)


이번엔 p4의 영역 중간에 3으로 덮어쓰여지게 된다.

how2heap의 이 자료에서는 
해제된 free의 SIZE를 조작하고 malloc 하는 방법을 택했다.

반대로
malloc된 청크의 사이즈를 조작한 후, 다시 free하고
malloc 하는 방식으로 똑같은 결과를 만들 수 있다.

이 방법은 The Forgotten Chunks 문서에 나와있다.

'Vulnerability_Tech > About Heap' 카테고리의 다른 글

(how2heap) - overlapping_chunks_2  (0) 2018.02.27
(how2heap) - house of lore  (0) 2018.02.21
(how2heap) - poison null byte  (0) 2018.02.20
malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
  오늘 분석한 how2heap 자료는 house of lore 이다.

이 공격을 할 수 있기 위해서는
가짜 청크 구조를 만들 수 있어야하고, 임의의 사이즈의 malloc과 free가 가능해야한다.

이 공격으로 스택영역의 주소를 malloc으로 받아서, return 주소를 덮어써서 임의의 코드를 실행해 볼 것이다.


(임의의 코드)


우리가 실행할 코드는 위의 jackpot코드이다. return 주소를 위 함수 주소로 덮어쓸 것이다.

먼저, 스택영역에 가짜 청크의 구조를 만들어야한다.


(가짜청크 영역)


가짜 청크 영역을 잡아둔다.

그 후 우리가 이용할 chunk를 하나 만든다.



(chunk 생성)


사이즈는 100으로 small bin 범위면 된다. 여기서는 small bin을 이용하였다.

알다시피, 정확한 chunk의 주소를 구하기 위해서는 2 WORD를 빼줘야한다.


(진짜 chunk 주소)


이유는
header가 2 WORD 이기 때문!이다.!

그 후 우리가 가짜 청크에 설정해주어야할 것이 있다.

이 모든 것은 아래의 small bin corruption check를 우회하기 위한 것이다.



(small bin corruption check)


위의 조건을 우회할 수 있도록, (걸리지 않도록)

가짜 청크에 값들을 넣어준다.



(조건 우회)



(메모리 모습)


가짜 청크의 모습을 정리해보면
스택버퍼1 - 스택버퍼2
이렇게 연결되어있다.

스택버퍼1 의 fd : victim
스택버퍼1의 bk : 스택버퍼2

스택버퍼2의 fd : 스택버퍼1

그 후 1000사이즈로 다시 malloc한다.



(malloc)


그 이유는
우리가 이제 victim 청크를 free할 것인데, top chunk와 병합되어버리지 않게 하기 위해
1000 사이즈 ( large bin ) 를 malloc 해주었다.

그리고 free!



(free)


victim 청크를 free한다.

그렇게 되면 아래와 같은 모습이 된다.



(free된 모습)


???
small bin에 들어가면 fd와 bk가 써져야 되는 거 아닌가??

=> 지금 안써진 이유는 바로 현재는 unsorted bin에 들어가있기 때문이다.
unsorted bin이란 일종의 캐시같은 역할인데, free가 되면 bin에 들어가기 전 unsorted bin에 들어가있는다. 그리고 그 다음 malloc때 같은 사이즈의 malloc이면 들어있던 청크가 반환되고 아니라면 원래 자기의 영역 bin에 들어가게 된다. 즉 한번의 재사용 기회가 주어지는 것이다.

그렇기에 우리는 지금 free 한 청크를 small bin에 넣고 싶기 때문에
이 한번의 재사용기회(?) unsorted bin에 있는 것을 빼주어야한다.

다른 large bin 영역의 사이즈로 다시 malloc 한다.



(malloc)


그리고 다시 victim 청크를 보면



(victim 청크)


fd와 bk가 작성되있는 것을 볼 수 있다.

아까 위에서
스택버퍼1 - 스택버퍼2 까지 연결했고,
스택버퍼1과 victim 청크를 연결할 것이다.
스택버퍼1의 fd는 아까 victim 청크 주소를 넣었고

이제 victim 청크의 bk주소에 스택버퍼1의 주소를 넣으면 된다.


(bk 조작)


그 후 victim 청크의 모습을 보면


(victim 청크)


bk가 스택버퍼1 의 주소로 조작된 것을 확인 할 수 있다.

이제 small bin의 사이즈 100인 bin에 우리가 만든
victim - 스택버퍼1 - 스택버퍼2
이렇게 주렁주렁 달려있을 것이다.

그러니 그대로 주르륵 꺼내보자.

사이즈는 100으로 malloc 한다.



(malloc)


malloc 하게 되면 victim 청크가 반환된다.
p3 = victim 청크

그러면 지금 스택버퍼1을 보면?


(스택버퍼1)


스택버퍼1의 fd가 바뀐것을 볼 수 있다.

이제 그다음 malloc(100)을 하면 우리의 스택버퍼1 차례이다.



(malloc)


p4에 스택버퍼1의 주소가 들어간다.



(결과)


그렇다면 이제 이걸 이용해서 ret 주소를 덮어쓸 것이다.


(ret 덮어쓰기)


원래 자료는 32비트에서 여서 그런지

직접 확인해보니 return 주소까지 72바이트 떨어진 자리였다.



(확인)


ret 주소에 jackpot 함수 주소가 들어가 있다.



(임의의 코드 실행)


결과적으로 마지막에 jackpot 함수(임의의 코드)가 실행되었다.

'Vulnerability_Tech > About Heap' 카테고리의 다른 글

(how2heap) - overlapping_chunks_2  (0) 2018.02.27
(how2heap) - overlapping_chunks  (0) 2018.02.22
(how2heap) - poison null byte  (0) 2018.02.20
malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17

+ Recent posts