2018. 6. 21. 04:42ㆍ0x04 pwnable/Linux Kernel Basic Study
0번 트랙, 0번 섹터를 부트섹터라고도 한다.
부트섹터 메모리 배치에 대해 알아보았다.
Q : 메모리 배치는 왜 하는건데?
A : 내부 코드들, 데이터들, 코드와 데이터 사이에 중복이 없도록 하기 위함이다.
Thinking...
중복 되면 마치 DOS(Derial Of Service)중 TearDrop과 같은 일이 벌어지지 않을까?
이 부분들은 주소가 많이 나와서 좀 더 명확하게 이해할 필요가 있다고 느낀다.
BIOS는 부트섹터를 메모리에 로드한다.
부트섹터는 나머지 두 섹터들을 메모리에 로드하기 전에 메모리 재배치를 수행한다.
부트섹터는 이러한 변수를 선언하게 된다.
SETUPLEN = 4 # Setup 프로그램 섹터 수
BOOTSEG = 0x07C0 # 부트섹터 위치
INITSEG = 0x9000 # 부트코드를 이 주소로 옮기는 용도로 쓰일 것 임
SETUPSEG = 0x9020 # 이 주소부터 셋업이 시작 됨
SYSSEG = 0x1000 # 시스템이 로드되는 주소 (65536)
ENDSEG = SYSSEG + SYSSIZE # 로드 끝내야 하는 지점인데 SYSSIZE는 어느정도인지 아직 잘 모르겠다
눈에 익혀야 하는 사진 한 장을 책에서 가져왔다.
리얼모드에서의 메모리 배치 방법에 대해 요약되어 있다.
그림 이해가 잘 되지 않았는데 저자님이 친절하게 코드를 작성해주셨다.
우선, 부트섹터를 복사하는 로직이 담겨 있는 코드이다.
mov ax, #BOOTSEG
mov ds, ax
mov ax, #INITSEG
mov es, ax
mov cx, #256
sub si, si
sub di, di
rep
movw # 2바이트 (WORD) 만큼 명령 반복 수행 == CX가 0이 될 때 까지
(DS:SI -> ES:DI)
summary:
DS(0x07C0) 값과 SI(0x0000)가 복사할 소스 어드레스 : 0x07C00
ES(0x9000) 값과 DI(0x0000)가 목적 어드레스인 0x90000을 가리킨다.
이 행위로 인해 부트섹터는 0x07C00 -> 0x90000으로 이동하게 된다.
즉, 메모리가 복사 되는 것이다.
256워드(512바이트) 만큼 복사가 진행된다는 것은 1 Sector를 뜻하기도 한다.
cf) C언어의 memcpy함수는 rep movsb로 주로 쓰이게 된다.
!! strcpy와 memcpy는 엄연히 다르다. !!
<복습>
strcpy는 길이 지정을 하지 않지만, source문자열이 반드시 \0(null)로 끝남
-> BOF에 취약하게 됨 -> 정해진 버퍼의 크기가 없기 때문
memcpy는 데이터 형에 관계없이 임의의 영역을 지정한 byte 수만큼 복사
-> 이 친구도 BOF에 취약함
-> size_t가 unsigned int인데 memcpy에 사용하는 3번째 인자 값이 signed 일 경우 실수로 -1을 넣었다고 가정 -> memcpy함수는 이 -1을 signed로 받아들이지 않고 unsigned로 받아들이게 됨 -> 고로 2,147,483,648보다 큰 수가 되어버림 -> 의도 하지 않은 메모리 크기가 복사 되어 버림
strcpy 원형 :
char *strcpy( char *strDestination, const char *strSource );
왜 strcpy의 두 번째 인자만 const일까?
const라는 것은 변하지 않는 상수 키워드인데
destination에 넘겨주는 인자는 문자열 상수가 될 수 없다.
"cf) 모든 포인터 변수는 초기화 되어야 한다"
그렇기 때문에 source에서도 문자열 변수, 상수는 넘겨줄 수 있지만, 초기화 되지 않은 포인터 변수는 사용할 수 없는 것이다.
memcpy 원형:
void *memcpy( void *dest, const void *src, size_t count );
memcpy는 반환 값이 void형 포인터 즉, 타입이 정해져 있지 않기 때문에 연산에는 사용될 수 없다.
<Secure Coding>
strcpy의 경우는 strcpy_s라는 함수를 이용하여 정해진 크기를 세팅해주어야 하고,
memcpy의 경우도 memcpy_s라는 것이 있다. memcpy는 src가 가리키는 곳 부터의 사이즈만 생각하지만 memcpy_s는 des 버퍼의 사이즈도 생각해준다.
위의 명령을 수행하면, 512바이트 만큼 메모리가 복사되어 0x90000의 내용과 0x07C00의 내용은 동일해진다. 이렇게 되는 이유가 "상호 관례" , "방향 인식" 때문이라고 한다.
방향 인식은 아직 잘 모르겠고, 상호 관례는 책을 보고 적긴 하는데 이게 왜 관례인지는 도통 모르겠습니다. 혹시나 이 글을 보는 사람 중에 아는 사람이 있으면 댓글을 작성해주시면 좋을 것 같습니다.
1. OS 입장에서의 관례
OS를 시작할 시작 프로그램을 부트섹터에 넣는다
2. BIOS 입장에서의 관례
부트섹터를 메모리의 0x07C00에 로딩한다.
어쨌든, 복사 되는 과정은 이해했으니 다음 코드를 진행해보았다.
rep
movw # 이 까지는 위의 코드 가져옴
jmpi go, INITSEG # 이때 까지 알고 있던 jmp 명령어는 이렇지 않았는데 ..
go: mov ax, cs
mov ds, ax
summary:
짚고 넘어가야 할 사항 : "jmpi go, INITSEG의 역할"
INITSEG : 새로운 어드레스
go: mov ax, cs가 진행되게 되면 cs는 0x07C0에서 0x9000으로 변경되게 된다.
INITSEG에 의해 새로운 어드레스 위치로 점프했지만,
JMPI go 명령을 만나기 전과 "동일한" 코드를 실행시킬 수 있는 장점이 생긴다.
헷갈리지 말자. 자칫하면 헷갈릴 것 같다..
사진도 가져왔다.
그 다음, 명령이다.
go: mov ax, cs
mov ds, ax # 위 코드 동일 -> ds는 0x9000, ip는 0x0000 -> 실제 주소는 0x90000이 됨. (이전 블로그 참고하면 알 수 있음)
mov es, ax # es역시 0x9000이 된다
mov ss , ax # ss역시 0x9000이 된다.
mov sp , #0xFF00
Summary:
ss : stack segment -> 이놈으로 스택을 관리할 수 있다.
지금 보면, cs 값을 ax에 복사하였고 그 ax 값이 ds, es, ss에 적용 되었기 때문에
cs, ds, es, ss는 동일한 값을 지니고 있다. (0x9000)
이러한 사진을 보면 스택은 0x9ff00에 설정됨을 알 수 있다.
내일은 Setup 프로그램을 메모리에 로드하는 과정을 공부하고 정리를 해볼까 한다.
'0x04 pwnable > Linux Kernel Basic Study' 카테고리의 다른 글
syscall 직접 만들어보기 (0) | 2020.04.18 |
---|---|
부트로더에 대한 간단한 정리 [3] (0) | 2018.06.22 |
부트로더에 대한 간단한 정리 (0) | 2018.06.21 |
[1] Why Vuln in this Code? (0) | 2018.06.17 |
A classification of Kernel Vulnerabilities (0) | 2018.06.17 |