개요 * Easy_CrackMe.exe 실습 리뷰 및 Easy_KeygenMe.exe 파일 실습 1. 디스어셈블리 상세 기능 - 스택 프레임 및 함수 호출 규약의 이해 - 동적 디버깅 사용 및 코드 실행 - 데이터 타입과 데이터 구조 - 기본 데이터/코드 변환 2. 스택/힙 할당 배열 및 구조체 접근
Byte 단위로 각각 스택에 넣어줌, 사용자가 입력한 문자열을 첫번째 글자부터 순서대로 Byte 단위로 레지스터에 넣음
CMP
esi가 3 이상이면 0으로 초기화
XOR
두 입력 신호가 서로 같으면 0, 다르면 1, 동일한 두 값 → 초기화
MOVZX
소스의 내용을 목적지로 복사, 목적지의 나머지 부분을 0 padding
(목적지에 32bit register → 소스에 16bit register)
MOVSX
소스의 내용을 목적지로 복사, 목적지의 나머지 부분을 소스의 맨 처음 bit로 채움
(목적지에 32bit register → 소스에 16bit register)
loc_40107E: pseudocode
2. 알고리즘 분석
1) v3이 v8의 strlen이 클 때 까지 반복하고, i가 3 이상이면 0으로 초기화
2) sprintf를 통해 Buffer에 생성된 serial 저장
3) "%s%02X"를 통해 기존 Buffer 내용과 v8 포인터 입력값을 한글자씩 가져옴
4) xor 연산을 한 값을 sprintf 함수를 통해 serial이 저장되는 Buffer에 저장
5) serial을 계산하여 scanf로 입력받은 후, strcmp 함수로 동일한지 비교
- if 0일 때 거짓, 0이 아닐 때 참으로 동작하므로, strcmp 내 0일 경우(문자열이 같을 경우) Correct 출력
v6에는 16 → 16 = 0x10
v7[i - 1]가 될 수 있는 값은, v7[0], v7[1], v7[2]의 반복
qmemcpy: sizeof(v7)만큼 " 0" 영역의 값을 v7로 복사
첫번째 인자(v7): 복사받을 메모리를 가리킴
두번째 인자(" 0"): 복사할 메모리를 가리킴
세번째 인자(sizeof(v7)): 복사할 데이터(값)의 길이(바이트 단위)
16진수이므로 두자리씩 끊어서 총 8자리
3. XOR(⊕) 연산의 결합법칙
^: XOR 연산자
p
q
p ⊕ q
(p ⊕ q) ⊕ q
T
T
F
T
T
F
T
T
F
T
T
F
F
F
F
F
(p ⊕ q) ⊕ q ≡ p ⊕ (q ⊕ q) ≡ p ⊕ F ≡ p
0x5b^0x10
0x13^0x20
0x49^0x30
0x77^0x10
0x13^0x20
0x5E^0x30
0x7D^0x10
0x13^0x20
4. 소스코드 작성(pseudo code 기반)
# sol1
serial = [0x5B, 0x13, 0x49, 0x77, 0x13, 0x5E, 0x7D, 0x13]
xor = [0x10, 0x20, 0x30]
ans = []
i_xor = 0
for i_ser in range(len(serial)):
if i_xor >= 3:
i_xor = 0
ans.append(serial[i_ser]^xor[i_xor])
i_xor += 1
print(chr(ans[i_ser]), end='')
# sol2
serial = [0x5B, 0x13, 0x49, 0x77, 0x13, 0x5E, 0x7D, 0x13]
xor = [0x10, 0x20, 0x30]
ans = []
for i in range(len(serial)):
ans.append(serial[i]^xor[i%3])
print(chr(ans[i]), end='')
개요 * Easy_CrackMe.exe 실습 리뷰 및 Easy_KeygenMe.exe 파일 실습 1. 디스어셈블리 상세 기능 - 스택 프레임 및 함수 호출 규약의 이해 - 동적 디버깅 사용 및 코드 실행 - 데이터 타입과 데이터 구조 - 기본 데이터/코드 변환 2. 스택/힙 할당 배열 및 구조체 접근
1) 함수 호출자가 파라미터 형태로 정보를 넘겨줄 때 어딘가에 저장되고, 호출된 함수가 정보를 찾게 해야 한다.
2) 함수의 작업을 위한 임시 저장소가 필요하다.
* 스택: 제한적으로 접근할 수 있는 자료 구조(LIFO, Last In First Out)
* 임시 저장소: 주로 지역 변수가 사용, 함수가 종료되면 더 이상 접근 불가
2. 레지스터: ESP, EBP, EIP, ECX, EAX
예제
32비트 x86 기반 컴퓨터의 컴파일된 함수 코드
void bar(int j, int k); // 호출할 함수
void demo_stackframe(int a, int b, int c) {
int x;
char buffer[64];
int y;
int z;
// 스택 프레임 테스트 용도 외에는 다른 목적이 없는 함수
bar(z, y);
}
지역 변수에 최소 76바이트(3개의 4바이트 정수, 64바이트 버퍼)가 필요
변수
오프셋
ESP 기반 스택 프레임
z
[esp]
지역 변수
y
[esp+4]
buffer
[esp+8]
x
[esp+72]
저장된 eip
[esp+76]
a
[esp+80]
파라미터
b
[esp+84]
c
[esp+88]
모든 오프셋은 ESP가 바뀜에 따라 다시 조절
변수
오프셋
EBP 기반 스택 프레임
z
[ebp-76]
지역 변수
y
[ebp-72]
buffer
[ebp-68]
x
[ebp-4]
저장된 ebp
[ebp]
저장된 레지스터
저장된 eip
[ebp+4]
a
[ebp+8]
파라미터
b
[ebp+12]
c
[ebp+16]
개념 정리
* E: Extended(16bit → 32bit system), 64bit에서는 R로 표현
ESP(stack pointer register)
: 현재의 스택 지점(stack의 가장 아래 부분), 스택의 크기를 조정할 때 사용
- 스택에 값을 넣을 때마다 ESP가 4만큼 줄어듦
EBP(base pointer)
: 프레임 포인터를 사용하는 함수를 가리킴(함수가 프레임 포인터를 쓰는지 여부의 분석이 필요할 때, 이 속성을 이용해 수작업으로 지정 가능 - 레지스터의 크기 보정 필요)
- 스택의 가장 윗 부분, 스택 프레임 형태로 저장된 함수의 지역 변수나 전달인자를 참조/변경시 사용하는 레지스터
- 고정적, 코드 유지에 용이(스택프레임 생성시)
EIP(instruction pointer)
: 실행할 명령의 주소
3. 어셈블리어: CMP, LEA, TEST
1) .text:004010B0
.text:004010B0 cmp [esp+68h+var_63], 61h ; 'a'
디스어셈블리 코드
설명
.text:00401080 var_63
스택 프레임에서 바로 참조할 수 있는 모든 변수의 목록을 변수 크기와 프레임 포인터에서 떨어진 거리와 함께 요약해 보여줌
1. IDA 파일 정보 확인 - IDA 로딩 파일(.idb), 데이터베이스 파일(.id0, .id1, .nam, .til) 2. IDA 툴 기능의 이해 - IDA 데스크탑/그래프 뷰 등 화면 동작/기능 파악 - 데이터 타입/코드 변환 등 설정 대화상자 기능 파악 - pseudo code 조회, 다른 함수로 변환 등 3. 코드 표기 이해(헥사 뷰, 디스어셈블리 뷰 동기화 등)