03 리버싱 시작하기 - 2. abex crackme 두 번째 예제

HnBrd·2023년 6월 19일
0

리버싱 입문

목록 보기
3/4
post-thumbnail

2.1. 프로그램 동작 방식

  • 목표 : 프로그램을 분석해서 사용자가 입력한 이름(Name)과 관련 있는 일련번호(Serial)을 찾아내라
    • 여기서는 이름을 기반으로 만들어진 일련번호가 저장되는 메모리 위치를 찾는 것을 목표로 한다
    • 사용자 입력으로부터 어떻게 일련번호가 만들어지는지는 분석하지 않음
  • 입력 항목
    • 이름
    • 일련 번호
  • 입력 항목을 모두 입력 후 <Check> 버튼을 누르면 일련번호가 맞는지 확인 창 표시

2.2. 프로그램 실행 (Paused & Running)

프로그램의 실행 과정

  1. Entry Point에서 실행 멈춤
  2. 디버거 오른쪽 아래 프로그램 상태가 "Paused"
  3. 실행 버튼(F9)을 눌러 프로그램 실행
  4. MSVBUM60.ThunRTMain() 함수에서 실행 중지
    • 이름을 통해 프로그램의 메인 함수임을 추정할 수 있다
  5. 프로그램 실행 상태는 "Running"
    • 프로그램과 상호작용(사용자 입력)하려면 반드시 실행 상태가 "Running"이어야 한다

2.3. 문자열 검색

디버거에는 문자열 검색 기능이 있다

  • 코드 양이 많아서 처음부터 한 줄씩 살펴보기 힘들 때
    • 문자열 검색을 사용하면 편하다
  • 코드 영역에서 마우스 우클릭
    • <Search For> → <All referenced strings>
    • 문자를 선언한 부분이나 문자를 사용하는 부분을 검색할 수 있다
  • 문자열 검색 화면에서 BP 설정이 가능하다

2.4. 코드 분석

  • 디버거 자동으로 어셈블러 코드를 분석해서 보기 편하도록 표시해 준다
    • 방해되는 경우가 있어서 <Remove analysis ...>을 통해 분석 내용을 제거 가능
      • <... from selection> : 선택한 행만 적용
      • <... from module> : 해당 모듈 내 모든 분석값 제거

2.5. 스택 분석하기

  • Stack의 역할
    • 변수를 스택에 저장해서 함수로 전달
    • 서브루틴에서 사용되는 변수를 저장하는 공간으로 활용
    • 서브루틴을 수행하기 전에 메인 루틴의 레지스터를 스택에 저장
  • 검색한 문자열을 중심으로 스택을 분석
    1. 문자열 검색 (ex. "Please enter at least 4 chars as name!")
    2. 문자열이 저장된 주소 확인 (ex. ”0040237C”)
    3. 주석 영역에서 주소에 실제 문자열이 저장되어있는 것을 확인
    4. 적절한 주소에 BP 설정 (ex. “00403054”)
    5. 프로그램 실행(F9)해서 프로그램 흐름이 BP가 설정된 부분으로 이동
    6. 스텝 오버(F8)를 사용해서 한 줄식 프로그램 실행
  • EIP 레지스터
    • 다음에 실행할 명령어 주소
    • 강제로 변경해서 프로그램 실행 위치 변경이 가능
    • OllyDbg 화면의 실행을 원하는 명령어에서 <New origin here>

2.6. 리틀 엔디안과 빅 엔디안

  • 스택 데이터 화면과 코드 화면에서의 데이터가 반대로 표시?
    • 엔디안 차이
    • 리틀 엔디안
      • 중요도 낮은 숫자부터
      • 1의 자리 숫자가 10의 자리 숫자보다 앞에
    • 빅 엔디안
      • 중요도 높은 숫자부터
      • 인간이 사용하는 숫자는 빅 엔디안
      • OllyDbg는 레지스터, 주석, 스택에 나오는 숫자는 사용자가 이해하기 쉽도록 빅 엔디안으로 표시

2.7. 메모리 분석하기

  • 프로그램 실행 중 모든 데이터는 메모리(Memory)에 저장
  • 디버거에서 메모리 표현하는 방식
    • ‘Address’, ‘Hex dump’, ‘ASCII’
      • Address : 메모리 주소를 표현하는 부분
      • Hex dump : 메모리에 저장된 데이터 (다른 메모리를 가리키는 주소 or 실제 데이터)
      • ASCII : Hex dump에 들어 있는 데이터를 ASCII 코드 값으로 변환한 값
    • 주소는 8자리 16진수로 표현
      • 16진수는 4비트로 표현 가능 → ‘0012F3B8’은 4 * 8 = 32bits
    • 데이터 (ex. D0 23 40 00)
      • 리틀 엔디안으로 표현된 데이터
    • 디버거는 ASCII 영역에서 Hex dump에 들어 있는 데이터를 ASCII 코드 값으로 변경
      • “{#@”과 같은 데이터는 대부분 다른 메모리를 가리키는 주소
    • 메모리 화면에서 16 Byte의 Hex dump 데이터
    • 한 줄 차이는 16진수 10만큼 = 10진수로 하면 16만큼 차이
  • 디버거로 열어서 직접 실행 시
    • 명령어를 우클릭하면 명령어가 사용하는 메모리와 스택 상태를 힌트 영역에서 확인 가능
    • 선택한 명령어가 [‘004023D0’을 스택의 특정 위치에 저장]
      • (= MOV DWORD PTR SS:[EBP-0E4], 004023D0)
      • <Follow in Dump> → …
        • <Memory Address>
          • ‘004023D0’이 저장된 위치를 찾아줌
            • 리틀 엔디안 방식으로 저장 (= D0 23 40 00)
        • <Immediate constant>
          • 메모리에 저장된 데이터를 직접 보여주며 바로 옆은 UNICODE로 바꿔서 문자열로 보여줌
            • 주소 ‘004023D0’ 에 저장된 데이터를 보여준다
        • <Selection>
          • 선택한 명령어(MOV DWORD PTR SS:[EBP-0E4], 004023D0)가 저장된 메모리 위치로 이동
    • <Edit> → <Binary edit>
      • 직접 메모리 값을 수정 가능

2.8. 프로그램 구조 분석

  • 가장 먼저 해야할 일은 열쇠가 되는 문자열을 검색하는 것이다

    • 검색할 문자열
      • Yep, this key is right
      • Nope, this serial is wrong
    • 코드 영역의 어디에서 사용되었는지 확인하고 주변을 중심으로 프로그램 코드를 분석하자
  • 분석하고 나서 알게 되는 것은 다음과 같은 프로세스 를 통해 일련번호를 검사한다는 것

    1. 입력한 이름의 길이를 검사해서 4자리보다 작으면 오류를 발생시킨다
    2. 이름을 이용해서 반복문으로 일련번호를 만든다
    3. 일련번호가 맞는지 확인한다

## 2.9. 문제 해결
  • 이름 : abcd / 일련번호 : 1234 를 입력한 뒤 프로그램 분석을 시작한다

  1. TEST AX, AX 구문에서 정답인지 오답인지 판단
    • 결과에 영향을 미치는 EAX 레지스터 하위 비트에 어떤 값이 있는지 알아야 한다
  2. 프로그램을 적절하게 실행시켜 메모리와 스택의 상태를 확인한다
  3. MOV DWORD PTR SS:[EBP-94], EAX
    • = EAX 레지스터가 가리키는 주소에 있는 데이터를 DWORD(4Byte)만큼 읽어서 EBP-94에 복사한다는 명령
  4. EBP-94 에 어떤 데이터가 있는지 확인
    • 메모리 값 주위를 살펴보면 EAX 레지스터에 저장된 값을 확인 가능
      • 입력한 1234 가 있음을 확인
    • C5C6C7C8 는 사용자가 입력한 이름 abcd과 일치하는 일련번호임을 알 수 있다!

profile
잡식

0개의 댓글