문제

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

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

예를 들어, assembly 코드를 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에는 모든 정규 수학 연산을 레지스터와 메모리에 수행할 수 있게 하는 많은 명령어가 있습니다. 단축 표현으로, A += B라고 하면, 이는 실제로 A = A + B를 의미합니다. 여기 몇 가지 유용한 명령어들이 있습니다:
add reg1, reg2       <=>     reg1 += reg2
sub reg1, reg2       <=>     reg1 -= reg2
imul reg1, reg2      <=>     reg1 *= reg2
div는 좀 더 어렵기 때문에 나중에 이야기하겠습니다.
참고: 모든 'regX'는 상수나 메모리 위치로 대체될 수 있습니다

다음을 수행하세요:
* rdi에 0x331337를 더하세요

이제 코드를 위해 다음을 설정할 것입니다:
rdi = 0x85d

assembly를 바이트 단위로 제공해 주세요 (최대 0x1000 바이트까지):

/challenge/run에서 나온 방식대로 어셈블리 코드를 작성하지 않고, nasm으로 제출하도록 하겠음.

 

문제 풀이

[bits 64]
add rdi, 0x331337

 

코드 분석

class ASMLevel2(ASMBase):
    """
    Modify register
    """

    init_rdi = random.randint(0, 0x1000)

    registers_use = True
    dynamic_values = True

    @property
    def description(self):
        return f"""
        Many instructions exist in x86 that allow you to do all the normal
        math operations on registers_use and memory. For shorthand, when we say
        A += B, it really means, A = A + B. Here are some useful instructions:
        add reg1, reg2       <=>     reg1 += reg2
        sub reg1, reg2       <=>     reg1 -= reg2
        imul reg1, reg2      <=>     reg1 *= reg2
        div  is a littler harder, we will discuss it later.
        Note: all 'regX' can be replaced by a constant or memory location

        Do the following:
        * add 0x331337 to rdi

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

    def trace(self):
        self.start()
        expected = self.init_rdi + 0x331337
        yield self.rdi == expected, f"rdi was expected to be {hex(expected)}, but instead was {hex(self.rdi)}"

 

힌트 출력

registers_use = True
dynamic_values = True
if self.registers_use:
    hints += """
    In this level you will be working with registers. You will be asked to modify
    or read from registers_use.
    """

if self.dynamic_values:
    hints += """
    We will now set some values in memory dynamically before each run. On each run
    the values will change. This means you will need to do some type of formulaic
    operation with registers_use. We will tell you which registers_use are set beforehand
    and where you should put the result. In most cases, its rax.
    """

해당 클래스 변수을 설정함으로 레지스터 사용 및 동적 변수 사용 했다는 것을 사용자에게 알려줌.

 

동적 변수

init_rdi = random.randint(0, 0x1000)
# ASMBase Class의 메소드

@property
def init_register_values(self):
    return {
        attr: getattr(self, attr)
        for attr in dir(self)
        if attr.startswith("init_") and attr[5:] in self.REG_MAP
    }

init_rdi 변수는 init_register_values에 의해 접근 가능하여, rdi 레지스터에 0 ~ 0x1000 사이의 값을 넣어줌.

 

@property
def description(self):
    return f"""
    Many instructions exist in x86 that allow you to do all the normal
    math operations on registers_use and memory. For shorthand, when we say
    A += B, it really means, A = A + B. Here are some useful instructions:
    add reg1, reg2       <=>     reg1 += reg2
    sub reg1, reg2       <=>     reg1 -= reg2
    imul reg1, reg2      <=>     reg1 *= reg2
    div  is a littler harder, we will discuss it later.
    Note: all 'regX' can be replaced by a constant or memory location

    Do the following:
    * add 0x331337 to rdi

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

해당 동적 변수는 description 메소드에 사용되어 사용자에게 rdi의 값을 보여줌.

 

정답 검증 로직

def trace(self):
    self.start()
    expected = self.init_rdi + 0x331337
    yield self.rdi == expected, f"rdi was expected to be {hex(expected)}, but instead was {hex(self.rdi)}"

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


이후, rdi 레지스터 값에 0x331337을 더한 값을 expected 변수에 삽입함.

 

덧셈의 값인 expectedself.rdi을 비교하여 같으면 (True, rdi was expected to be [정답 결과], but instead was [제출 결과])를 출력하게 됨.

복사했습니다!