[모각코] Reversing(마무리)
08-18
crackme를 통한 디버거 사용법-1
예제
문제: 2개의 정수를 입력받아 값이 특정 조건을 만족하면 correct!을 출력하고
틀리면 wrong!을 출력
문제 풀이 - 0
프로그램에서 input:
을 출력하고, 숫자 2개를 입력받고, 무언가
받은 숫자를 처리하는 코드를 실행한 다음, 그 결과에 따라
correct!
나 wrong!
을 출력하는 순서로 실행됨
따라서 main
함수에서 printf
, puts
등의 출력 함수 및
scanf
같이 입력을 받는 함수가 포함되었을 것임
문제 풀이 - 1 메인함수 찾기
x64dbg에서 모듈 간 호출 찾기 기능을 사용해 main
함수를 찾음
문제 풀이 - 2 메인함수 분석
번호별로 다음과 같음
-
스택을 확장하는 코드, 이 함수에서 스택을 0x38만큼 스택을
사용한다는 것을 알 수 있음 -
첫 번째 인자에
input:
문자열의 주소를 넣고sub_140001070
함수 호출
내부로 들어가 분석할 수 있지만, 이 프로그램에서는 인자만으로
printf
이거나 이와 비슷한 함수라는 것을 알 수 있어 넘어가도 됨
-> 이처럼 적당히 추측하고 넘어가는 것이 분석 시간을 단축하는 데 있어
중요한 요소 중 하나
-
첫 번째 인자에
%d %d
문자열의 주소를 넣고, 두 번째 인자에rsp+0x14
세 번째 인자에rsp+0x20
을 넣고sub_140001120
을 호출 -
첫 번째 인자에
rsp+0x24
, 두 번째 인자에rsp+0x20
을 넣고
sub_140001180
를 호출 -
sub_140001180
함수의 리턴값인eax
를 확인해 0이면 점프를 뛰어
wrong!
이 출력되고 1이면 점프를 뛰지 않고correct!
을 출력 -
main
함수의 리턴값을 0으로 설정하고 확장한 스택을 정리한 후 리턴
1|140001200 | sub rsp,38 |
2|140001204 | lea rcx,qword ptr ds:[140002230] | 140002230:"input: "
2|14000120B | call <easy-crackme1.sub_140001070> |
3|140001210 | lea r8,qword ptr ss:[rsp+20] |
3|140001215 | lea rdx,qword ptr ss:[rsp+24] | rdx:EntryPoint
3|14000121A | lea rcx,qword ptr ds:[140002238] | 140002238:"%d %d"
3|140001221 | call <easy-crackme1.sub_140001120> |
4|140001226 | mov edx,dword ptr ss:[rsp+20] |
4|14000122A | mov ecx,dword ptr ss:[rsp+24] |
4|14000122E | call <easy-crackme1.sub_140001180> |
5|140001233 | test eax,eax |
5|140001235 | je easy-crackme1.140001246 |
5|140001237 | lea rcx,qword ptr ds:[140002240] | 140002240:"correct!"
5|14000123E | call qword ptr ds:[<&puts>] |
5|140001244 | jmp easy-crackme1.140001253 |
5|140001246 | lea rcx,qword ptr ds:[14000224C] | 14000224C:"wrong!"
5|14000124D | call qword ptr ds:[<&puts>] |
6|140001253 | xor eax,eax |
6|140001255 | add rsp,38 |
6|140001259 | ret |
문제 풀이-3 sub_140001180분석
디스어셈블 결과 jmp
와 jcc
코드가 복잡해 보이면 x64dbg의 그래프 기능 이용
문제 풀이-4 그래프로 분석
선의 의미를 잘 생각하며 각각 노드에 대한 분석
- 1번 노드(시작 부분)
인자로 받은 ecx
와 edx
를 각각 rsp+0x8
과 rsp+0x10
에 저장
- 9번 노드
함수의 끝 노드로 확장한 스택을 정리하고 ret
하는 코드 밖에 없음
- 6, 7, 8번 노드
9번 노드와 연결된 노드들로, 함수의 리턴값인 eax
를 설정함
6번과 8번 노드는 eax
를 0으로, 7번 노드는 eax
를 1로 설정
1 -> 2 -> 3 -> 4 -> 5 -> 7 -> 9
다음과 같은 흐름으로 진행되면 메인함수에서 correct!
가 출력됨
- 1번 -> 2번 노드
cmp
명령어 후 분기하는 것을 확인할 수 있음
2번 노드로 가기 위해서는 점프를 뛰지 말아야 하고 ja
명령어 이므로
rsp+0x20
가 0x2000보다 작거나 같아야 함
- 2번 -> 3번 노드
1번 노드와 비슷한 명령어로 분기함
- 3번 -> 4번 노드
mov eax,dword ptr ss:[rsp+20] ; eax = 첫 번째 인자
imul eax,dword ptr ss:[rsp+28] ; eax = eax * 두 번째 인자
mov dword ptr ss:[rsp],eax ; [rsp] = eax
xor edx,edx ; edx = 0
mov eax,dword ptr ss:[rsp+20] ; eax = 첫 번째 인자
div dword ptr ss:[rsp+28] ; eax = edx:eax / 두 번째 인자
mov dword ptr ss:[rsp+4],eax ; [rsp+4] = eax
mov eax,dword ptr ss:[rsp+28] ; eax = 두 번째 인자
mov ecx,dword ptr ss:[rsp+20] ; ecx = 첫 번째 인자
xor ecx,eax ; ecx = ecx ^ eax
mov eax,ecx ; eax = ecx
mov dword ptr ss:[rsp+8],eax ; [rsp+8] = eax
cmp dword ptr ss:[rsp],6AE9BC ; [rsp]와 0x6ae9bc을 비교한다.
jne easy-crackme1.1400011F1 ; Jump short if not equal
이를 정리하면 다음과 같음
eax = 첫 번째 인자
eax = eax * 두 번째 인자
[rsp] = eax
edx = 0
eax = 첫 번째 인자
eax = edx:eax / 두 번째 인자
[rsp+4] = eax
eax = 두 번째 인자
ecx = 첫 번째 인자
ecx = ecx ^ eax
eax = ecx
[rsp+8] = eax
# [rsp]와 0x6ae9bc을 비교한다.
이를 코드로 정리하면,
[rsp] = 첫 번째 인자 * 두번째 인자
[rsp+4] = 첫 번째 인자 / 두번째 인자
[rsp+8] = 첫 번째 인자 ^ 두 번째 인자
#[rsp] 와 0x6ae9bc를 비교함
따라서 3번 노드에서 4번 노드로 가기 위해서는 명령어가 jne
이고
첫 번째 인자 * 두 번째 인자가 0x6ae9bc 이여야 함
- 4번 -> 5번 노드
3번 노드에서 설정한 [rsp+4]
가 4인지 비교하고 점프
5번 노드로 가려면 `[rsp+4]가 4여야 함
- 5번 -> 7번 노드
3번 노드에서 설정한 [rsp+8]
가 0x12fc인지 비교하고 점프
7번 노드로 가려면 명령어가 jne
이고 빨간선이니 [rsp+8]
이 0x12fc여야 함
문제 풀이 - solve
for x in range(0x2000 + 1):
y = x ^ 0x12fc
if x * y != 0x6ae9bc:
continue
if x // y != 4:
continue
print("answer: ",x y)
crackme를 통한 디버거 사용법 - 2
예제: 어떤 값을 입력 받아 특정 조건을 만족하면 correct!
를 출력하고
틀리면 wrong!
출력
문제풀이 - 0
프로그램 실행 방식 추측
- 메인함수를 찾음
- 입력의 형식이 어떤 형태인지 알아냄
- 입력받은 값을 어떤 방식으로 처리하는지 알아냄
문제풀이 - 1
모듈간 분석을 진행한 후 문자열 찾기를 눌러 문자열 분석을 실행
input:
이라는 문자열이 있는 것을 확인할 수 있음
문제풀이 - 2
- 스택 확장
malloc(0x10)
을 호출, 그 결과를[rsp+0x20]
에 저장- rdi에 2의 주소를 복사한뒤, eax를 0으로 설정하고 ecx를 0x10으로
설정한 다음rep stosb
명령어를 실행 - 인자에
input:
주소를 넣고sub_140001060
를 호출 _fgetchar
를 호출 이 값은 al에 저장되며140001334
에서 사용하게 됨- 하나씩 분석하면 다음과 같음
ecx = 1
rcx = rcx * 0
rdx = buf // [rsp+0x20]
[rdx + ecx] = al
_fgetchar() return value
- 각각 고정된 값을 넣음을 의미
- 첫번째 인자에
[rsp+0x20]
을 넣고sub_1400011E0
를 호출한 다음
그 결과가 0보다 크면correct!
, 0이면wrong!
을 출력 - 리턴값을 0ㅇ으로 설정하고 스택을 정리한 뒤 리턴
#1
1400012F0 | push rdi |
1400012F2 | sub rsp,30 |
#2
1400012F6 | mov ecx,10 |
1400012FB | call qword ptr ds:[<&malloc>] |
140001301 | mov qword ptr ss:[rsp+20],rax |
#3
140001306 | mov rdi,qword ptr ss:[rsp+20] |
14000130B | xor eax,eax |
14000130D | mov ecx,10 |
140001312 | rep stosb |
#4
140001314 | lea rcx,qword ptr ds:[140003250] | 0000000140003250:"input: "
14000131B | call <easy-crackme2.sub_140001060> |
#5
140001320 | call qword ptr ds:[<&_fgetchar>] |
#6
140001326 | mov ecx,1 |
14000132B | imul rcx,rcx,0 |
14000132F | mov rdx,qword ptr ss:[rsp+20] | rdx:EntryPoint
140001334 | mov byte ptr ds:[rdx+rcx],al |
#7
140001337 | mov eax,1 |
14000133C | imul rax,rax,1 | rax:EntryPoint
140001340 | mov rcx,qword ptr ss:[rsp+20] |
140001345 | mov byte ptr ds:[rcx+rax],64 | 64:'d'
140001349 | mov eax,1 |
14000134E | imul rax,rax,2 | rax:EntryPoint
140001352 | mov rcx,qword ptr ss:[rsp+20] |
140001357 | mov byte ptr ds:[rcx+rax],77 | 77:'w'
14000135B | mov eax,1 |
140001360 | imul rax,rax,3 | rax:EntryPoint
140001364 | mov rcx,qword ptr ss:[rsp+20] |
140001369 | mov byte ptr ds:[rcx+rax],73 | 73:'s'
14000136D | mov eax,1 |
140001372 | imul rax,rax,4 | rax:EntryPoint
140001376 | mov rcx,qword ptr ss:[rsp+20] |
14000137B | mov byte ptr ds:[rcx+rax],71 | 71:'q'
14000137F | mov eax,1 |
140001384 | imul rax,rax,5 | rax:EntryPoint
140001388 | mov rcx,qword ptr ss:[rsp+20] |
14000138D | mov byte ptr ds:[rcx+rax],61 | 61:'a'
140001391 | mov eax,1 |
140001396 | imul rax,rax,6 | rax:EntryPoint
14000139A | mov rcx,qword ptr ss:[rsp+20] |
14000139F | mov byte ptr ds:[rcx+rax],77 | 77:'w'
1400013A3 | mov eax,1 |
1400013A8 | imul rax,rax,7 | rax:EntryPoint
1400013AC | mov rcx,qword ptr ss:[rsp+20] |
1400013B1 | mov byte ptr ds:[rcx+rax],64 | 64:'d'
1400013B5 | mov eax,1 |
1400013BA | imul rax,rax,8 | rax:EntryPoint
1400013BE | mov rcx,qword ptr ss:[rsp+20] |
1400013C3 | mov byte ptr ds:[rcx+rax],75 | 75:'u'
#8
1400013C7 | mov rcx,qword ptr ss:[rsp+20] |
1400013CC | call <easy-crackme2.sub_1400011E0> |
1400013D1 | test eax,eax |
1400013D3 | je easy-crackme2.1400013E4 |
1400013D5 | lea rcx,qword ptr ds:[140003258] | 0000000140003258:"correct!"
1400013DC | call qword ptr ds:[<&puts>] |
1400013E2 | jmp easy-crackme2.1400013F1 |
1400013E4 | lea rcx,qword ptr ds:[140003264] | 0000000140003264:"wrong!"
1400013EB | call qword ptr ds:[<&puts>] |
#9
1400013F1 | xor eax,eax |
1400013F3 | add rsp,30 |
1400013F7 | pop rdi |
1400013F8 | ret |
문제풀이 - 3, 메인 함수 분석
앞서 분석한 것을 정리하면 다음과 같은 코드
int main() {
char* buf = malloc(0x10); //임의의 이름 buf
memset(buf, 0, 0x10);
sub_140001060("input: "); // sub_140001060 is printf like function
buf[0] = _fgetchar();
buf[1] = 'd';
buf[2] = 'w';
buf[3] = 's';
buf[4] = 'q';
buf[5] = 'a';
buf[6] = 'w';
buf[7] = 'd';
buf[8] = 'u';
if(sub_1400011E0(buf)) {
puts("correct!");
}
else {
puts("wrong!");
}
return 0;
}
문제풀이 - 4, sub_1400011E0 분석
-
인자로 받은
rcx
를 스택에 저장하고 스택을 확장
스택에 저장 될때[rsp+8]
에 저장됐지만, 스택을 확장하면서
rsp의 값이 바뀌기 때문에 확장 이후부터는[rsp+0x50]
로 접근해야
인자에 접근 가능, 이를 편의를 위해arg1
이라고 임의로 명명 -
변수를 0으로 초기화 한 후 8바이트로 부호있는 확장을 거친 후
arg1
의 인덱스로 사용하여 값을 가져옴
그후 가져온 값을 첫번째 인자로 넣어 sub_1400010C0
를 호출한 다음,
리턴값을 [rsp+0x20]
에 저장, 편의를 위해 임의로 tmp1,
[rsp+0x24]
를 tmp2를 임의로 명명
- tmp2를 가져와 1을 증가한 후 다시 저장
#1
1400011E0 | mov qword ptr ss:[rsp+8],rcx |
1400011E5 | sub rsp,48 |
#2
1400011E9 | mov dword ptr ss:[rsp+24],0 |
1400011F1 | movsxd rax,dword ptr ss:[rsp+24] |
1400011F6 | mov rcx,qword ptr ss:[rsp+50] |
1400011FB | movzx ecx,byte ptr ds:[rcx+rax] |
1400011FF | call <easy-crackme2.sub_1400010C0>|
140001204 | mov dword ptr ss:[rsp+20],eax |
#3
140001208 | mov eax,dword ptr ss:[rsp+24] |
14000120C | inc eax |
14000120E | mov dword ptr ss:[rsp+24],eax |
-
tmp2를 가져와서 arg1의 인덱스로 사용하여 값을 가져온 다음
해당 값이 0인지 확인, 만약 0일 경우1400012CA
로 점프 -
4에서
arg1[counter(tmp2)] == 0
일때 이 부분으로 오게 됨
해당 부분은 tmp1의 값이 0x5b일 경우 1을, 아닐 경우 0을 리턴하는 코드
이 부분에서 sub_1400011E0
함수가 1을 리턴해야 메인함수에서 correct!
를
출력하기 때문에 tmp1의 값은 0x5b가 되어야함
#4
140001212 | movsxd rax,dword ptr ss:[rsp+24] |
140001217 | mov rcx,qword ptr ss:[rsp+50] |
14000121C | movzx eax,byte ptr ds:[rcx+rax] |
140001220 | test eax,eax |
140001222 | je easy-crackme2.1400012CA |
...
#5
1400012CA | cmp dword ptr ss:[rsp+20],5B | 5B:'['
1400012CF | jne easy-crackme2.1400012DB |
1400012D1 | mov dword ptr ss:[rsp+30],1 |
1400012D9 | jmp easy-crackme2.1400012E3 |
1400012DB | mov dword ptr ss:[rsp+30],0 |
1400012E3 | mov eax,dword ptr ss:[rsp+30] |
1400012E7 | add rsp,48 |
1400012EB | ret |
-
4에서
arg1[counter]==0
를 만족하지 않을 때 이 부분으로 오게 되며
counter를 가져와서 arg1의 인덱스로 사용하여 값을 가져온 다음 해당 값을
[rsp+0x34]
에 넣음, 이를 편의를 위해 tmp3로 명명 -
counter를 가져와 1을 증가한 후 arg1의 인덱스로 사용하여 값을 가져옴
이후 해당 값을 첫 번째 인자로sub_1400010C0
를 호출한 후
결과값을[rsp+0x2c]
에 저장 이를 편의를 위해 tmp4로 명명 -
[rsp+0x34]
에 저장했던 값을[rsp+0x28]
에 저장
편의를 위해 이를 tmp5로 명명 -
분기문들의 집합으로 tmp5값을 비교하여 다음과 같이 분기함
if(tmp5 == 'a') goto #10;
else if(tmp5 == 'd') goto #12;
else if(tmp5 == 'f') goto #13;
else if(tmp5 == 's') goto #11;
else goto #14;
-
tmp4와 tmp1 값을 가져오 두 값을 더한 후 다시 tmp1에 저장
이후 14로 이동 -
tmp4와 tmp1 값을 가져와 두 값을 뺀 다음 다시 tmp1에 저장
이후 14로 이동 -
tmp4와 tmp1 값을 가져와 두 값을 곱한 다음 다시 tmp1에 저장
이후 14로 이동 -
tmp4와 tmp1의 값을 가져와 두 값을 나눈 다음 다시 tmp1에 저장
이후 14로 이동 -
counter를 가져와 2를 더한 후 다시 counter에 저장 이후 4로 점프
#9
14000125C | cmp dword ptr ss:[rsp+28],61 | 61:'a'
140001261 | je easy-crackme2.14000127A | -> #10
140001263 | cmp dword ptr ss:[rsp+28],64 | 64:'d'
140001268 | je easy-crackme2.14000129E | -> #12
14000126A | cmp dword ptr ss:[rsp+28],66 | 66:'f'
14000126F | je easy-crackme2.1400012AD | -> #13
140001271 | cmp dword ptr ss:[rsp+28],73 | 73:'s'
140001276 | je easy-crackme2.14000128C | -> #11
140001278 | jmp easy-crackme2.1400012BA | -> #14
#10
14000127A | mov eax,dword ptr ss:[rsp+2C] |
14000127E | mov ecx,dword ptr ss:[rsp+20] |
140001282 | add ecx,eax |
140001284 | mov eax,ecx |
140001286 | mov dword ptr ss:[rsp+20],eax |
14000128A | jmp easy-crackme2.1400012BA | -> #14
#11
14000128C | mov eax,dword ptr ss:[rsp+2C] |
140001290 | mov ecx,dword ptr ss:[rsp+20] |
140001294 | sub ecx,eax |
140001296 | mov eax,ecx |
140001298 | mov dword ptr ss:[rsp+20],eax |
14000129C | jmp easy-crackme2.1400012BA | -> #14
#12
14000129E | mov eax,dword ptr ss:[rsp+20] |
1400012A2 | imul eax,dword ptr ss:[rsp+2C] |
1400012A7 | mov dword ptr ss:[rsp+20],eax |
1400012AB | jmp easy-crackme2.1400012BA | -> #14
#13
1400012AD | mov eax,dword ptr ss:[rsp+20] |
1400012B1 | cdq |
1400012B2 | idiv dword ptr ss:[rsp+2C] |
1400012B6 | mov dword ptr ss:[rsp+20],eax | -> #14
#14
1400012BA | mov eax,dword ptr ss:[rsp+24] |
1400012BE | add eax,2 |
1400012C1 | mov dword ptr ss:[rsp+24],eax |
1400012C5 | jmp easy-crackme2.140001212 | -> #4
문제 풀이, sub_1400010C0 분석
#1
1400010C0 | mov byte ptr ss:[rsp+8],cl |
1400010C4 | sub rsp,38 |
#2
1400010C8 | mov dword ptr ss:[rsp+20],0 |
1400010D0 | movsx eax,byte ptr ss:[rsp+40] |
1400010D5 | mov dword ptr ss:[rsp+24],eax |
1400010D9 | mov eax,dword ptr ss:[rsp+24] |
1400010DD | sub eax,65 |
1400010E0 | mov dword ptr ss:[rsp+24],eax |
#3
1400010E4 | cmp dword ptr ss:[rsp+24],14 |
1400010E9 | ja easy-crackme2.140001167 | -> #4
#5
1400010EB | movsxd rax,dword ptr ss:[rsp+24] |
1400010F0 | lea rcx,qword ptr ds:[140000000] |
1400010F7 | mov eax,dword ptr ds:[rcx+rax*4+1184] |
1400010FE | add rax,rcx |
140001101 | jmp rax |
...
#4
140001167 | lea rcx,qword ptr ds:[140003240] | 140003240:"wrong input!\n"
14000116E | call <easy-crackme2.sub_140001060> |
140001173 | xor ecx,ecx |
140001175 | call qword ptr ds:[<&exit>] |
#6
14000117B | mov eax,dword ptr ss:[rsp+20] |
14000117F | add rsp,38 |
140001183 | ret |
-
인자로 받은
c1
을[rsp+8]
에 저장하고 스택을 확장
[rsp+8]
에 저장되었지만 스택이 확장되었기 때문에 인자에 접근하려면
[rsp+0x40]
으로 접근해야함, 이를 편의를 위해 arg1으로 명명 -
[rsp+0x20]
을 0으로 초기화하고 arg1을 가져와[rsp+0x24]
에 넣고
다시 해당 값을 가져와 0x65만큼 뺀 다음 다시[rsp+0x24]
에 저장
편의를 위해[rsp+0x20]
을 tmp1,[rsp+0x24]
를 tmp2라 명명 -
tmp2를 가져와 0x14보다 크면 4로 점프
-
첫 번째 인자로
wrong input!\n
문자열의 주소를 지정한 후
sub_140001060
를 호출한 다음exit
함수를 호출
5.
하나씩 의사코드로 변형한 후 정리하면 다음과 같음
rax = tmp2 // [rsp+24]
rcx = 0x140000000
eax = [rcx+rax*4+0x1184]
rax += rcx
goto rax
goto 0x140000000 + [0x140000000 + tmp2 * 4 + 0x1184]
- tmp1을 리턴값으로 설정하고 스택을 정리한 후 리턴
5번 추가 분석
goto 0x140000000 + [0x140000000 + tmp2 * 4 + 0x1184]
0x140000000의 의미는 언뜻 보면 무엇인지 알 수 없지만 분석한 코드가
전부 0x14000…로 시작하는 걸 통해 프로그램의 베이스 주소임을 눈치챌 수 있음
따라서 해당 코드의 의미는 0x140000000 + tmp2 * 4 _ 0x1184에서
4바이트 값을 가져와서 다시 0x140000000를 더한 후 점프
0x140001184
에 있는 점프테이블을 하나씩 따라가 보면 전부 다음과 같은
코드 영역으로 점프를 뛰는 형태
140001103 | mov dword ptr ss:[rsp+20],1 |
14000110B | jmp easy-crackme2.14000117B | -> #6
14000110D | mov dword ptr ss:[rsp+20],2 |
140001115 | jmp easy-crackme2.14000117B | -> #6
140001117 | mov dword ptr ss:[rsp+20],3 |
14000111F | jmp easy-crackme2.14000117B | -> #6
140001121 | mov dword ptr ss:[rsp+20],4 |
140001129 | jmp easy-crackme2.14000117B | -> #6
14000112B | mov dword ptr ss:[rsp+20],5 |
140001133 | jmp easy-crackme2.14000117B | -> #6
140001135 | mov dword ptr ss:[rsp+20],6 |
14000113D | jmp easy-crackme2.14000117B | -> #6
14000113F | mov dword ptr ss:[rsp+20],7 |
140001147 | jmp easy-crackme2.14000117B | -> #6
140001149 | mov dword ptr ss:[rsp+20],8 |
140001151 | jmp easy-crackme2.14000117B | -> #6
140001153 | mov dword ptr ss:[rsp+20],9 |
14000115B | jmp easy-crackme2.14000117B | -> #6
14000115D | mov dword ptr ss:[rsp+20],0 |
140001165 | jmp easy-crackme2.14000117B | -> #6
모두 tmp1을 어떤 값으로 설정한 다음 6으로 점프로 뛰는 코드
tmp2 | tmp1 |
---|---|
0 | 3 |
1 | x |
2 | x |
3 | x |
4 | 8 |
5 | x |
6 | x |
7 | x |
8 | x |
9 | x |
10 | 9 |
11 | 0 |
12 | 1 |
13 | 4 |
14 | x |
15 | 5 |
16 | 7 |
18 | 2 |
19 | x |
20 | 6 |
x로 된 부분은 4로 점프뛰는 부분
지금까지 분석한 것을 정리하면 6~38번째 라인이 점프테이블 관련 코드이며
특정 값들(0, 4, 10, 11, 12, 13, 15, 16, 18, 20) 은 전부 tmp1을
어떤 값으로 설정하게 되고, 나머지 값들은 fail 라벨로 점프를 뜀
함수는 어떤 문자 한글자를 받아 다음과 같은 규칙에 따라 숫자를 리턴하는 함수
q - 1 w - 2 e - 3 r - 4 t - 5 y - 6 u - 7 i - 8 o - 9 p - 0
코드 해석
메인 함수에서 사용자로부터 입력을 한 글자 받아 dwsqawdu
문자열과 합친 다음,
sub_1400011E0
을 호출 한 후 문자열을 숫자와 산술기호로 변환하여 연산을 진행
연산 결과가 0x5b(91)과 같다면 correct!
를 출력함
참고자료
dreamHack Reverse Engineering