memory corruption stack
TRANSCRIPT
Memory Corruption
김태우 (http://codevania.tistory.com)
데브루키 (http://cafe.naver.com/devrookie.cafe)
INDEX
• 개요• 메모리 손상 탐지• 스택 손상–스택 오버런–비동기 동작과 스택 포인터–호출 규약 불일치
• 회피 전략
개요
• 메모리 손상이 수정하기 어려운 이유
–원인과 결과를 관련짓기 어렵다•손상의 원인과 현상이 너무나도 떨어져 있다
–오류 재발을 일관성 있게 할 수 없다•비정상적인 조건에서 발생하는 증상
개요
• 메모리 손상은 언제 발생하는가 ?–실행 스레드가…
•자신이 소유하지 않은 메모리 블록에 쓴다•자신이 소유한 메모리 블록에 쓰지만 ,
이 메모리 블록의 상태를 손상한다
개요
• 메모리 손상의 증상
–크래쉬•최선의 시나리오•메모리 손상의 출처가 어디인지를 나타내기 때문
–넌크래쉬와 예측 불가능한 동작•최악의 시나리오•사용되는 유효한 메모리 블록에 쓰기•메모는 손상을 당했고 이후는 예측 불가능•출처를 찾기란 매우 난해
개요
• 어떻게 해야 하나 ?–즉시 통지
•소유하지 않은 메모리에 쓸 때 즉시 통지를 받아야 함
• 사후탐지–올바른 전략–강력한 툴 셋
개요
• 레지스터– EAX, EBX, ECX, EDX
• A: Accumulator register• B: Base register• C: Count register• D: Data register
– ESI, EDI• S: Source (for memory operation) Register• D: Destination (for memory operation) Register
– EIP, ESP• I: Instruction Pointer• S: Stack Pointer
8 16 32 64
AL AX EAX RAX
메모리 손상 탐지
메모리 손상 탐지
• State Analysis–진짜로 메모리 손상으로 인한 것인지 확인–크래쉬라면
•유효한 코드 경로를 거쳐 문제에 도달 가능
–넌크래쉬라면•메모리 상태를 비정상적으로 만드는 코드는 찾기 힘듬•어딘가에서 메모리 손상이 발생했다고 보는 것이
유일한 그럴듯한 (-_-) 설명임
메모리 손상 탐지
• Source Code Analysis–메모리 손상
•스레드가 소유하지 않은 메모리 위치에 쓸 때 발생•즉 , 쓰여지는 데이터는
이를 쓰는 스레드에게만 의미가 있음•이 데이터를 분석하고 이해•가능한 의혹들의 범위를 축소
메모리 손상 탐지
• Use Memory Corruption Detection Tool– Declaim
•메모리 손상을 잡아내는 것을 보장하지는 못함•메모리 손상 시나리오를 잡는 데 도움을 줌
– Tool•컴파일러
– 스택 검증 코드 삽입 가능
•어플리케이션 베리파이어– 힙 기반의 메모리 손상의 경우 최선의 툴
메모리 손상 탐지
• Instrument Source Code–가능성에 대한 이론 수립
•지금까지 모은 모든 정보를 이용
–이론이 옳고 그름을 증명해야함
메모리 손상 탐지
• Define Avoidance Strategy–향후의 회피 전략을 정의
•가장 중요함•툴을 이용
– 코드가 잠재적인 메모리 손상을 최소화하기 위한 명시적인 조치를 취하도록
– 일반적인 메모리 손상을 잡아내도록
메모리 손상 탐지
메모리 손상 탐지
메모리 손상 탐지
스택 손상
• 스택–단순한 데이터 구조체–동작은 항상 최상단에서 일어남– LIFO 구조
• Last In First Out
• 코드와 연관해서…–실행중인 스레드에 할당된 메모리 블록–목적 : 함수 호출 체인을 추적하는 것
예제 . StackDesc
예제 . StackDesc
예제 . StackDesc
예제 . StackDesc
• 함수의 도입부– ebp
•주어진 프레임의 베이스 포인터를 포함•각 프레임마다 유지되어야 함
– 새 프레임의 생성 ( 호출명령 ) 전에 스택에 저장됨
– esp ebp •새로운 스택의 시작을 만들기 위해
예제 . StackDesc
예제 . StackDesc
예제 . StackDesc
예제 . StackDesc
예제 . StackDesc
예제 . StackDesc
• 8?– Printf 함수 복귀시 esp 는 호출 준비 작업으로
스택에 저장됐던 마지막 인자를 가리키게 설정– 호출 이전의 상태로 스택 복구를 보장하기 위해
8 바이트 (2*4= 두 인자의 크기 )
스택손상
• 스택 오버런• 비동기 동작과 스택 포인터• 호출 규약 불일치
스택 오버런
• 스택 오버런–예약된 자신의 호출 스택 일부분을 덮어쓰는 경우
•복귀 주소•전체 프레임•스택을 완전히 소진
–결과•크래시•예측 불가능한 행위•심각한 보안 결함
– 공격자가 컴퓨터의 완전한 제어를 획득하게 해줄 수 있음
스택 오버런
스택 오버런
스택 오버런
스택 오버런
스택 오버런
• 해결책–지역 변수를 위해 할당된 크기 이상을 변수에 복사하지 않게 보장•연결 스트링이 크기 제한이 없는 가변 길이
– 스택에 메모리를 할당하는 것이 잘못되었음– 컴파일 시점에 스트링의 크기를 알아야함– 이럴 때는 힙에서 버퍼를 할당
•연결 스트링이 실제로 30문자로 제한 ( 아이디 / 비번 등 )– 경계를 넘지 않게 보장– 스트링의 크기를 지정하게 돼있는 스트링 복사 함수를 사용
스택 오버런
• 도움을 줄만한 툴– Visual Studio 코드분석
•컴파일 타임에 에러를 탐지• Team Suit 만 ;
– PREfast•http://www.microsoft.com/whdc/DevTools/tools/PREfast
.mspx•http://bwahn.tistory.com/tag/PREfast
비동기 동작과 스택 포인터
• 지역 변수의 수명에 대한 잘못된 가정–매우 흔한 프로그래밍 실수 중의 한 가지–표준 호출 규약의 경우
•함수의 종결부 코드 실행시•스택 포인터는 이전 프레임으로 리셋되고•모든 지역 변수는 유효하지 않다고 간주됨
비동기 동작과 스택 포인터
• 에러가 안 나서 디버깅을 할 수가 =_=;;
호출 규약 불일치
• 호출 규약–함수 호출자와 피호출 함수 간의 단지 규약일 뿐–규약간의 주 차이점
•인자가 호출 함수로 전달되는 방식•스택에서 이 인자가 정리되는 방식
호출 규약 불일치
호출 규약 불일치
호출 규약 불일치
• stdcall 이 더 좋잖아 ? cdecl 은 언제 사용 ?–가변인자리스트를 지원– stdcall 은 callee 가 전달된 인자의 수를 알 수 없음
호출 규약 불일치
호출 규약 불일치
호출 규약 불일치
• 미완
회피 전략
• /GS– C/C++ 코드생성 버퍼보안검사–리턴 주소가 덮어 써졌는지 보안 검사 코드 추가–바이러스 작성이 어려워짐–하지만 단순한 안전장치로만 생각 . 완벽한 보호는
불가능 .
회피 전략
• /RTC (Run Time Checks)– C/C++ 코드생성 작은 형식 검사 (/RTCc)
•RTCc ( 데이터 손실 보호 )– 작은 데이터형식 변환 시 손실 검사 :
» 모든 지역 변수를 0xCC 로 초기화» 스택 프레임 검사가 로컬 변수 언더런 / 오버런을 감지» 스택 충돌에 대해서 스택 포인터 검증
– C/C++ 코드생성 기본 런타임 검사 (/RTCu)•RTCu (초기화되지 않은 변수 보호 )
– 초기화 되지 않은 변수가 접근될 때마다 에러 표시– 변수 초기화를 하지 않는 것은 흔한 실수인데 이를 방지
– RTC 옵션은 디버그 빌드에만 영향이 있음
회피 전략
• 툴– Rational’s Purify– NuMega’s BoundsChecker
Summary
• 스택 손상–심각한 불안정성 문제를 유발–무작위적인 크래시–사용자의 컴퓨터를 손상–심각한 보안 취약점 제기
• 자세–개발자는 스택 무결성 유지–소프트웨어의 성공을 위해 회피 기법을 채택