문제

ASMLevel5에 오신 것을 환영합니다
==================================================

어떤 레벨과 상호작용하기 위해서는 프로그램에게 원시 바이트를 stdin을 통해 보내야 합니다.
이 문제들을 효과적으로 해결하기 위해서는, 먼저 실행하여 무엇이 필요한지 확인한 다음
바이트를 조립하고 이 프로그램에게 파이프로 보내야 합니다.

예를 들어, 만약 asm.S 파일에 어셈블리 코드를 작성한다면, 다음과 같이 오브젝트 파일로 조립할 수 있습니다:
as -o asm.o asm.S

그리고 나서, .text 섹션(코드)을 asm.bin 파일로 복사할 수 있습니다:
objcopy -O binary --only-section=.text asm.o asm.bin

최종적으로, 챌린지에게 보낼 수 있습니다:
cat ./asm.bin | /challenge/run

모든 것을 한 번에 실행할 수도 있습니다:
as -o asm.o asm.S && objcopy -O binary --only-section=.text ./asm.o ./asm.bin && cat ./asm.bin | /challenge/run

이 레벨에서는 레지스터와 작업하게 될 것입니다. 수정하거나
레지스터에서 읽어야 할 것입니다.

이제 각 실행 전에 메모리에 몇몇 값을 동적으로 설정할 것입니다. 각 실행에서
값들은 변경될 것입니다. 이는 여러분이 레지스터와 어떤 형태의 공식 연산을 필요로 한다는 것을 의미합니다. 어떤 레지스터가 미리 설정되어 있는지 알려드리고,
결과를 어디에 둬야 하는지도 말씀드릴 것입니다. 대부분의 경우에는, 그것은 rax입니다.



어셈블리에서의 모듈로는 또 다른 흥미로운 개념입니다! x86은 나눗셈을 한 뒤의
나머지를 얻을 수 있게 해줍니다. 예를 들어:
10 / 3  ->  나머지 = 1
이전에 소개된 명령어를 통해 div 명령어를 사용하여 나눗셈의 나머지를 얻을 수 있습니다.
대부분의 프로그래밍 언어에서 우리는 '%' 기호로 모듈로를 나타냅니다.

다음을 계산해 주세요:
rdi % rsi
값을 rax에 넣어주세요.

코드를 준비하기 위해 다음과 같이 설정할 것입니다:
rdi = 0x34b766ba
rsi = 0xf

어셈블리 바이트를 제공해 주세요 (최대 0x1000 바이트):

 

문제 풀이

[bits 64]
mov rax, rdi
div rsi
mov rax, rdx

 

코드 분석

class ASMLevel5(ASMBase):
    """
    Modulo
    """

    init_rax = 0xFFFFFFFFFFFFFFFF
    init_rdi = random.randint(1000000, 1000000000)
    init_rsi = 2 ** random.randint(2, 16) - 1

    registers_use = True
    dynamic_values = True

    @property
    def description(self):
        return f"""
        Modulo in assembly is another interesting concept! x86 allows you to get the
        remainder after doing a division on something. For instance:
        10 / 3  ->  remainder = 1
        You can get the remainder of a division using the instructions introduced earlier
        through the div instruction.
        In most programming languages we refer to mod with the symbol '%'.

        Please compute the following:
        rdi % rsi
        Place the value in rax.

        We will now set the following in preparation for your code:
        rdi = {hex(self.init_rdi)}
        rsi = {hex(self.init_rsi)}
        """

    def trace(self):
        self.start()
        expected = self.init_rdi % self.init_rsi
        yield self.rax == expected, f"rax was expected to be {hex(expected)}, but instead was {hex(self.rax)}"

해당 코드들은 밑 레벨의 코드와 비슷하기 때문에 trace 메소드만 설명하도록 하겠음.

 

정답 검증 로직

def trace(self):
    self.start()
    expected = self.init_rdi % self.init_rsi
    yield self.rax == expected, f"rax was expected to be {hex(expected)}, but instead was {hex(self.rax)}"

self.start 메소드를 호출하여 제출한 어셈블리 바이너리를 실행함.

 

동적 변수들을 한 나눈 나머지 결과를 expected 변수에 삽입함.

 

이후, 제출한 어셈블리 바이너리 실행 후의 rax 레지스터 값과 expected 변수를 비교하여 같으면, (True, rax was expected to be [정답의 곱셈 결과], but instead was [제출한 곱셈 결과])을 반환하게 됨.

복사했습니다!