Index
문제
실행 파일(exe)이 주어지고, 실행하면 사용자 입력을 받고 프로그램이 종료된다.
문제풀이
문제의 main 함수이다. 이전 rev-basic-3
문제와 같은 main 형태를 가지고 있다. 바로 sub_140001000
함수로 들어가보자.
rev-basic-3
문제를 풀면서 알다싶이, for문 안에 if 구문을 역계산하면 문제가 해결된다.
if ( ((unsigned __int8)(16 * *(_BYTE *)(a1 + i)) | ((int)*(unsigned __int8 *)(a1 + i) >> 4)) != byte_140003000[i] )
디컴파일 된 복잡한 if 구문이다. 이를 편하게 해석하면 다음과 같다.
if (16 * a1[i] | a1[i] >> 4)) != byte_140003000[i] )
<<
또는 >>
은 비트를 옮기는 시프트 연산자 이다.
만약 a1[0]
의 값이 24
라면, bit로 나타내면 0001 1000
이다.
24 >> 4
는 오른쪽으로 4칸을 옮겨서 0000 0001
이 된다.
24 << 4
는 왼쪽으로 4칸 옮겨서 1 1000 0000
이 되지만, 1byte
라면 1000 0000
이 된다.
전구의 내용을 적용하면 조금 더 이쁘게 수식을 만들 수 있다.
if (a1[i] << 4 | a1[i] >> 4)) != byte_140003000[i] )
이를 역 연산 하면 문제를 해결할 수 있다.
byte_140003000
에 어떤 값이 들어가있는지 F2
로 브레이크 포인터를 걸고 F9
로 실행시켜 메모리 주소로 찾아가서 확인했다.
byte_7ff6522f3000
주소로 찾아가면 저장된 hex
값을 확인할 수 있다.
파이썬 풀이 코드
str = [0x24, 0x27, 0x13, 0xc6, 0xc6, 0x13, 0x16, 0xe6, 0x47, 0xf5, 0x26, 0x96, 0x47, 0xf5, 0x46, 0x27, 0x13, 0x26, 0x26, 0xc6, 0x56, 0xf5, 0xc3, 0xc3, 0xf5, 0xe3, 0xe3]
for i in range(len(str)):
print(chr((str[i]) >> 4 | (str[i]) << 4), end="")
실행 결과
Synchronize IDA Assembly
우리는 문제를 풀었지만, 더 성장하기 위해 조금 더 생각하고 공부해볼 것이다.
도구가 해주는 디컴파일로 문제를 쉽게 풀었지만, 실제 어떤 어셈블리어들이 사용되면서 연산되는지 비교해보자.
IDA에서는 디컴파일 코드와 실제 어셈블리어 코드를 sync하여 현위치를 알 수 있다. 오른쪽 버튼을 클릭하고 Synchronize with 메뉴로 연결시켜준다.
이후 IDA View를 나타내는 그래프를 끌어서 화면 오른쪽이나 왼쪽에 적절히 배치한다.
그림처럼 왼쪽에는 디컴파일된 코드, 오른쪽에는 어셈블리어로된 그래프가 나타난다. 이를 통해 어느 부분이 수행되는지 확인 가능하다.
for문 안의 if 구문의 어셈블리어 코드이다. 주요하게 볼 곳은 sar
명령어, shl
명령어 부분이다.
두 명령어 모두 시프트 연산자이고, sar
은 오른쪽 시프트 연산자, shl
은 왼쪽 시프트 연산자이다.
먼저, 14000102d
에서 어디선가 값을 가져와서 eax
에 저장하고 이 값을 오른쪽으로 4칸 시프트 연산한다. 이후, 14000104d
에서 아까와 같은 값을 가져와서 ecx
에 저장하고 왼쪽으로 4칸 시프트 연산한다.
ecx
는 여기서 끝이 아니라 0xF0
과 and
연산을 수행한다. 그러면 ecx
의 값의 하위 4비트는 0으로 고정된다(aaaa 0000
). 또한 eax
는 오른쪽으로 4칸 시프트했으므로, 상위 4비트는 0으로 고정된다(0000 bbbb
).
이후, eax
(0000 bbbb)와 ecx
(aaaa 0000)를 or
연산 한다. 연산 결과는 (aaaa bbbb
)가 된다. 이 결과가 byte_140003000[i]
와 일치해야 한다.
문제를 푸는 것도 중요하지만, 디테일하게 이해하고 가는 것이 좋으니까 길게 작성해봤다.
다음은 어셈블리어 코드를 생각하고 푼 파이썬 코드이다.
파이썬 풀이 코드
str = [0x24, 0x27, 0x13, 0xc6, 0xc6, 0x13, 0x16, 0xe6, 0x47, 0xf5, 0x26, 0x96, 0x47, 0xf5, 0x46, 0x27, 0x13, 0x26, 0x26, 0xc6, 0x56, 0xf5, 0xc3, 0xc3, 0xf5, 0xe3, 0xe3]
for i in range(len(str)):
eax = ecx = str[i]
print(chr((ecx & 0xf0) >> 4 | (eax & 0x0f) << 4), end="")
실행 결과
Uploaded by N2T