리비전 이분검색?
- 버그없는 리비전과 버그 있는 리비전을 알면
- 가운데 리비전을 받아서 버그가 재현되는지 본다
- 재현되면 앞쪽 절반, 재현안되면 뒤쪽 절반을 반복
두 명이 동시에 재현하면 삼분검색 가능!
13.
그러나…
재현조건이 애매했다
• 보통20분 정도면 재현되었지만
• 40분 동안 재현이 안 되어도 버그가 없다는 뜻은 아니다
마감 직전이면 인력을 동원해서 해결하겠지만…
무엇보다, 지루한 작업
14.
가설: 다른 스레드가덮어썼나?
해당 객체를 건드리는 모든 코드를 리뷰함:
값을 덮어쓰는 곳이 없었다. 멀티스레드 문제는 아니다.
디스어셈블리를 보자
15.
나 어셈 볼줄 모르는데…
인텔 어셈블리에 대해 알고 있던 것:
• 함수 호출할 때 인자를 스택에 푸시한다 (역순)
• 함수 호출할 때 레지스터를 스택에 푸시해서 보관한다
• 가상함수 호출 메커니즘
• mov는 복사, call은 함수호출 자세한 건 몰랐음
• 데이터 브레이크포인트라는 것이 있고, 코드에서 제어할 수 있다
잠깐 가상함수 호출절차
실제 멤버변수들
VTable 시작 주소
class X의 객체 가상함수 A 주소
가상함수 B 주소
가상함수 C 주소
가상함수 D 주소
가상함수 E 주소
가상함수 F 주소
class X의 VTable
Visual C++에서의
Machine code
X::가상함수 C 코드
19.
디스어셈블리를 보자
edi에 sim이저장되어 있다고 가정하는 코드다.
가상함수 테이블을 조회하다가 죽었구나!
↖vtbl 주소를 eax에 저장
← GameContext 함수 주소를 얻어서
←호출
20.
디버거 Watch에 (GameSimulation*)edi를 입력해보니
실로 멀쩡한 객체를 가리키는 값이 아니었다
edi는 함수 맨 앞에서 설정했고(mov edi,dwordptr[ebp+8])
이후에 edi를 덮어쓰는 곳이 없다
그런데 edi가 이상한 값이 되어 있다…?
디스어셈블리를 보자
21.
구글 검색 <edipush 책임> 해봄
호출되는 쪽에서 edi를 쓰기 전에 저장할 책임이 있다고 한다
edi 저장 책임은 누가 지나?
22.
누가? 어떻게?
레지스터를 깨먹는함수라니 듣도 보도 못했다
이 함수에는 58건의 call이 있다. 누굴까?
일일이 들여다보는 건 시간 낭비라고 판단
탐침을 박아보자
누군가가 edi를 깨먹고 있다
23.
모든 함수호출 전에edi를 저장하고
모든 함수호출 후의 edi와 비교해서
달라지면 브레이크포인트
탐침을 박아보자
24.
여러가지 방법을 시도해봄
뭘추가하면 레지스터 배치가 달라져서
탐침이 동작 않는 경우가 부지기수
“변수 선언 순서 바꾸니까 되는데요”가 바로 이런 경우
탐침을 박고 빌드한 뒤 어셈블리를 봐서
의도한 대로 동작하는지 확인해봐야 함
탐침을 박아보자
이 경우 스택의변조를 감지해야 한다
VS에서 수동으로 매번 잡았다 풀었다 할 순 없다;;
코드에서 데이터 브레이크포인트 설정/해제가 가능
참고:http://dual5651.hacktizen.com/tt/entry/BreakBreak-BreakPoint
없어진문서이지만http://web.archive.org/를통해접근가능
데이터 브레이크포인트?
34.
edi가 push된 곳을어떻게 알지?
구글: intel assembly stack top register → esp
• edi를push한직후에
• 디버그Watch로*(int*)esp를찍어보니…
• edi내용이똑같이저장되어있다
• 됐다!
메모리 변경을 감지하자