문제 링크
https://dreamhack.io/wargame/challenges/18
풀이
해당 문제에서 주요한 부분은 위 그림 1과 같다.
해당 로직을 디컴파일(F5)하여 확인하면 위와 같다.
사용자 입력 값을 대상으로 4비트 이동 ( >> 4 )을 하고, 16을 곱하기 ( << 4 )도 하여 or 연산을 한 것과 기존 정답 배열과 비교를 한다.
정답 배열의 메모리 주소가 가리키는 값은 위 그림 3과 같다.
Python>list(get_bytes(0x00007FF659DF3000,28))
[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, 0x0]
이제, 위 그림 1과 그림 2를 참고하여 복잡한 연산의 결과로 0x24가 나오려면 어떻게 해야하는지 아래에서 살펴본다.
먼저, 0x24 는 0010 0100(2) 이다.
위 그림 1을 보면, or eax, ecx 연산이 있는데 이 때 0010 0100이 eax에 저장되어야 한다. 여기서 ecx 를 잠시 생각해보면, 바로 위에서 and ecx, 0F0h 를 한다. 그리고 그 앞에서 shl ecx, 4 연산을 했다.
ecx 도 사용자 입력 값 1바이트이므로 "B"를 입력했다했을 때, 0x42 에서 shl ecx, 4를 하면 ecx 레지스터에 0x420이 저장되는 것을 볼 수 있다.
그 후 and ecx, 0F0h를 하면 ecx에는 0x20 ( RCX 0000000000000020 )이 남는다.
즉 위 그림 3의 배열의 각 원소 값들로 올바른 사용자 입력 값 ( 0x00000000 )의 마지막 4비트 ( 0000 )는 알 수 있다는 것이다. 이제 그 앞 상위 4비트를 알아내야 한다.
sar eax, 4 연산 부분이 있는데 사용자 입력 값을 오른쪽으로 4비트 밀어버린다. 이 값과 and ecx, 0F0h 한 값과 or 연산을 하므로, 우리가 찾던 4비트 ( 0000 )가 0100(2)라는 것을 알 수 있게 된다.
즉 첫번째 사용자 입력해야 할 값은 "B"이다. 또, 두번째 입력 값을 구했을 때 0x72인 것을 알 수 있었고, 아래와 같이 코드를 작성할 수 있다.
byte = [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, 0x0]
# 각 바이트 값을 자릿수 뒤집어서 변환
for i in range(0,28):
swapped_bytes = [((b & 0x0F) << 4) | ((b & 0xF0) >> 4) for b in byte]
# 결과를 문자열로 join하여 출력
print("".join(chr(b) for b in swapped_bytes))
어셈블리 연산 하나 하나에 집중해서 좀 어렵게 접근했는데, 처음부터 크게 보았다면 쉽게 풀 수 있는 문제라는 생각이 든다..