흰싸라기 2022. 3. 13. 23:45

crackme : 크랙 연습 목적으로 작성되어 공개된 프로그램

  • crackme 샘플을 분석하여 디버거와 디스어셈 코드에 익숙해질 수 있음

 

6.1 abex’ crackme #1

6.1.1 프로그램 실행

: 디버깅 전 파일을 실행하여 어떤 프로그램인지 살펴보기

 

1) 프로그램 실행

  • “Make me think your HD is a CD-Rom” 메시지 박스 출력
    • CD-ROM : CD를 이용해 정보를 기억시키고, 저장하고 검색할 수 있는 컴퓨터 데이터 저장기구

  • CD-ROM을 통해 HD가 HDD(Hard Disk Drive)를 의미한다고 추측할 수 있음

2) 1번 메시지창 확인 버튼 클릭

  • Error 메시지 박스 출력 후 종료

6.1.2 디버깅 시작

: OllyDbg를 통해 abex’ crackme #1 실행

 

  • EP에 main 함수가 바로 나타남
    • 어셈블리 언어로 만들어진 실행파일이기 때문
    • 개발툴을 사용하면 작성한 소스코드 외에 컴파일러가 *Stub Code를 추가하기 때문에 디스어셈을 하면 복잡하게 보임
    • *Stub Code : 고급언어로 구현된 바이너리 파일을 실행하기 위해 어셈블리 언어로 작성한 코드
    • 에섬블리 언어로 작성하면 어셈 코드가 곧 디스어셈 코드가 됨

6.1.3 코드 분석

1) Win32 API 호출 위주로 살펴보기

MessageBox("Make me think your HD is a CD-Rom")
GetDriveType("C:\\\\")
..
MessageBox("Nah... This is not a CD-ROM Drive!")
MessageBox("OK, I really think that yout HD is a CD-ROM! :p")
ExitProcess()
  • GetDriveType("C:\\”) : C드라이브의 타입을 얻어옴
  • 조작을 통해 CD-ROM 타입으로 인식하도록 만들어 "OK, I really think that yout HD is a CD-ROM! :p" 메시지 박스가 출력되도록 만들기

2) 코드 상세 분석

: 분석을 통한 에셈블리 명령어 익히기

; MessageBoxA() 호출 
00401000 >/>PUSH 0                       ; /Style = MB_OK|MB_APPLMODAL
00401002  |>PUSH 402000                  ; |Title = "abex' 1st crackme"
00401007  |>PUSH 402012                  ; |Text = "Make me think your HD is a CD-Rom."
0040100C  |>PUSH 0                       ; |hOwner = NULL
0040100E  |>CALL 00401061                ; \\MessageBoxA

; GetDriveType() 호출
00401013  |>PUSH 402094                  ; /RootPathName = "c:\\"
00401018  |>CALL 00401055                ; \\GetDriveTypeA

0040101D  |>INC ESI                      ; ESI = 0 ; 값을 1을 증가 
0040101E  |>DEC EAX                      ; EAX = 2 ; 값을 1 감소 
0040101F  |>JMP SHORT 00401021           ; 의미없는 JMP 명령(garbage code)
00401021  |>INC ESI                      ; ESI = 1
00401022  |>INC ESI                      ; ESI = 2
00401023  |>DEC EAX                      ; EAX = 1

;조건 분기(401028 또는 40103D)
00401024  |>CMP EAX,ESI                  ; EAX과 ESI 비교 
00401026  |>JE SHORT 0040103D            ; JE(Jump if Equal) 조건 분기 명령
                                         ; 두 값이 같으면 0040103D로 점프 
                                         ; 다르면 그냥 밑 401028로 진행
                                         ; 0040103D 주소는 제작자가 원하는 메시지 박스 출력코드   
 
; 실패 MessageBoxA() 호출 
00401028  |>PUSH 0                       ; /Style = MB_OK|MB_APPLMODAL
0040102A  |>PUSH 402035                  ; |Title = "Error"
0040102F  |>PUSH 40203B                  ; |Text = "Nah... This is not a CD-ROM Drive!"

00401034  |>PUSH 0                       ; |hOwner = NULL
00401036  |>CALL 00401061                ; \\MessageBoxA
0040103B  |>JMP SHORT 00401050           ;  abexcm1-.00401050

; 성공 MessageBoxA() 호출 
0040103D  |>PUSH 0                       ; |/Style = MB_OK|MB_APPLMODAL
0040103F  |>PUSH 40205E                  ; ||Title = "YEAH!"
00401044  |>PUSH 402064                  ; ||Text = "Ok, I really think that your HD is a CD-ROM! :p"
00401049  |>PUSH 0                       ; ||hOwner = NULL
0040104B  |>CALL 00401061                ; |\\MessageBoxA

00401050  \\>CALL 0040105B                ; \\ExitProcess
  • 에셈블리 명령어 정리
명령어 설명
PUSH 스택에 값을 입력
CALL 지정된 주소의 함수를 호출
INC 값을 1 증가
DEC 값을 1 감소
JMP 지정된 주소로 점프
CMP 주어진 두개의 operand(피연산자 : 연산에 사용되는 변수나 상수) 비교
*SUB 명령어와 동일하나 operand 값이 변경되지 않고 EFLAGS 레지스터만 변경됨
(두 operand의 값이 동일하다면 ZF = 1로 세팅)
JE 조건 분기(Jump if equal)
*ZF = 1이면 점프

 

6.2 크랙

: 코드를 패치(다른 코드로 덮어 쓰는 행위)하여 프로그램을 크랙하기

  • 조건 분기(JE) 명령어를 점프(JMP) 명령어로 변경
    • 무조건 점프하기 때문에 목적 주소로 이동 가능
  • ‘JE SHORT 0040103D’ 명령어를 Assemble 기능[Space]을 이용하여 ‘JMP SHORT 0040103D’ 명령어로 변경

 

6.3 스택에 파라미터를 전달하는 방법

: 코드를 통해 함수 호출 시 스택에 파라미터를 전달하는 방법

00401000 >/>PUSH 0                       ; /Style = MB_OK|MB_APPLMODAL
00401002  |>PUSH 402000                  ; |Title = "abex' 1st crackme"
00401007  |>PUSH 402012                  ; |Text = "Make me think your HD is a CD-Rom."
0040100C  |>PUSH 0                       ; |hOwner = NULL
0040100E  |>CALL 00401061                ; \\MessageBoxA

위 어셈블리 코드를 C 언어로 변역하면 아래와 같다

MessageBox(NULL, "Make me think your HD is a CD-Rom.", "abex' 1st crackme"); 
  • 실제 C언어 소스코드에서 함수에 넘기는 파라미터의 순서가 어셈블리 언어에서는 역순으로 넘어간다
    • 스택 메모리 구조(FILO : First In Last Out, LIFO : Last In First Out)

 

스택

  • 함수의 첫째 파라미터가 스택의 제일 위에 취하고 마지막 파라미터가 아래에 쌓임
  • 따라서, 스택에서 파라미터를 하나씩 꺼낼 때 FILO 구조에 따라 첫번째 파라미터부터 꺼낼 수 있다
    • 함수 입장에서는 스택에 파라미터가 순서대로 들어 있는 셈