System Hacking/assembly

[assembly] 어셈블리어로 별피라미드 출력하기-(2)

jir4vvit 2020. 7. 10. 18:00

환경 : 칼리리눅스 2019.2

 

* 어셈블리코드에서 outer를 outter로 오타가 났다.. 블로그 포스팅 중에는 이를 무시하고 outer라는 네이밍으로 설명하겠다.. 

 

jiravvit.tistory.com/entry/assembly-%EC%96%B4%EC%85%88%EB%B8%94%EB%A6%AC%EC%96%B4%EB%A1%9C-%EB%B3%84%ED%94%BC%EB%9D%BC%EB%AF%B8%EB%93%9C-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0-1

 

[assembly] 어셈블리어로 별피라미드 출력하기-(1)

환경 : 칼리리눅스 2019.2 칼리 리눅스에서 어셈블리어를 이용하여 별 피라미드를 출력해보도록 하자. 아래와 같은 피라미드를 옆으로 눕힌 모양(?)을 찍어보도록 하겠다. 별 피라미드를 찍는다면

jiravvit.tistory.com

저번시간에 이어서 작성해보도록 하겠다. 저번시간에는 최초의 _start함수를 작성해보았다. _start에서는 사용자가 매개변수로 피라미드 높이를 입력받고 내가 사용할 레지스터들을 초기화해주는 작업이 있었다. 

 

 


우리는 아래의 피라미드를 출력해야 한다.

 

어셈블리어로 출력해보기 전에 java 코드를 다시 한 번 더 확인해보자.

public class Main {

    public static void main(String[] args) {
	// write your code here

        int n = 5;

        // upPyramid
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < i; j++) {
                System.out.print("*");
            }
            System.out.println();
        }

        // downPyramid
        for(int i = n; 0 < i; i--) {
            for(int j = i; 0 < j; j--) {
                System.out.print("*");
            }
            System.out.println();
        }
        
    }
}

출력결과는 아래처럼 나오게 된다.

Main.java내가 어셈블리 코드를 작성하면서 가장 인상깊었던 점은 어셈블리 코드는 java와 같은 언어 보다 한 눈에 들어오지 않는다는 점이다. java나 c같은 언어는 반복문이며, 반복문 조건이며 어셈블리 코드에 비해서는 한 눈에 보인다. 하지만 어셈블리 코드는 아니다.

 

아래는 위의 java 코드에서 upPyramid 부분의 안쪽 for문(_upinner)이다. 이처럼 upPyramid의 바깥 for문(_upouter), downPyramid의 안쪽 for문(_downinner), 바깥 for문(_downouter). 이렇게 총 4개의 함수가 있다.

 upPyramid 부분의 안쪽 for문(_upinner)

upPyramid의 안쪽 for문은 피라미드의 별(*)을 직접 출력하는 부분이다.

 

처음에 r10과 r9를 비교하고 있다. r10은 Main.java에서 j이고 r9는 i이다. 초기엔 그 둘은 0이다. 그래서 바로 아래 명령으로 이동해 _upouter 함수로 이동하게 된다.

 

rax dummy
rsi STAR
r8 5 (우리가 입력한 수)
r9 0
r10

 

 upPyramid 부분의 바깥 for문(_upouter)

upPyramind의 바깥 for문이다. r9(i)은 현재 0이고 r8(n)은 우리가 입력한 수 5이다. 다르기때문에 바로 아래가 아닌 그 밑으로 가게 된다. rsi에 EMPTY를 넣어주지만 rax값에는 sys_write를 의미하는 1이 들어가있지않아서 줄바꿈을 출력하지않는다. 그 아래 줄에서야 rax를 1로 세팅하여 write 시스템 콜을 설정하고, r10(j)를 0으로 초기화하고 r9(i)를 1을 더해주면서 java 코드에서의 i++와 같다는 것을 생각할 수 있다. 그리고 다시 _upinner로 가게 된다.

 

rax 1 (WRITE)
rsi EMPTY
r8 5 (우리가 입력한 수)
r9 1
r10

 

 upPyramid 부분의 안쪽 for문(_upinner)

다시 r10(j)와 r9(i)를 비교한다. r9(i)가 아까 +1해줬으므로 r9(i)는 1이다. r10과 r9가 다르기때문에 두 줄 내려가서 rsi에 STAR을 세팅하고 시스템콜을 호출하여 별을 하나 출력한다. 그리고 r10(j)를 1증가 시킨다. 이렇게 되면 r10(j)와 r9(i)는 둘 다 1이된다. 이것을 반복해주기 위해서 다시 _upinner을 호출한다.

 

이번에는 r10과 r9의 값이 같으므로 _upouter 함수로 이동하여 줄바꿈을 해줘야한다.

 

rax 1 (WRITE)
rsi STAR
r8 5 (우리가 입력한 수)
r9 1
r10 1

 

여기까지 했으면

*

이렇게 별 하나가 찍혀있을 것이다. 그리고 커서는 *바로 오른쪽에 존재하게 된다.

 

 

 upPyramid 부분의 바깥 for문(_upouter)

r9와 r8이 다르기 때문에 바로 아래줄은 건너뛰고 더 아래로 간다. rsi에 EMPTY를 넣어주고 시스템콜을 불러준다. rax가 1이기때문에 정상적으로 줄바꿈이 된다. 다시 rax를 1로 설정해주고 r10(j)를 0으로 설정해준다. r10(j)를 0으로 설정해주는 이유는 위의 Main.java코드를 보면 이해할 수 있다. 안쪽 for문에 진입할때마다 int j = 0;으로 초기화 시켜주고 있기 때문이다. 그리고 r9(i)에 다시 1을 더해주고 _upinner로 이동한다.

 

rax 1 (WRITE)
rsi EMPTY
r8 5 (우리가 입력한 수)
r9 2
r10 0

 

여기까지 했으면

*

 

이렇게 될 것이다. 줄바꿈까지 된 상태이다.

 

다시 _upinner로 가보자.

 

 upPyramid 부분의 안쪽 for문(_upinner)

r10(j)은 0이고 r9(i)는 2다. 코드 전체를 생각해보면 아래쪽에서 r10이 1 증가하면서 _upinner을 계속 반복하게 된다. 그리고 종료조건은 맨 첫줄의 r10과 r9를 비교하여 둘이 같으면 je _upouter에 _upouter(줄바꿈)를 호출하게 된다. 다시 생각해보면 r10이 1 증가하여 0과 1일때만 이 반복문이 돌게 된다. 두바퀴를 돈다는 것이다. 그러면 시스템콜에 의해 *이 두개 찍히게 된다.

 

rax 1 (WRITE)
rsi STAR
r8 5 (우리가 입력한 수)
r9 2
r10 2

 

*

** 

이렇게 출력이 되고 커서는 두번째줄인 **의 오른쪽에 존재하게 된다.

 

r9가 계속 증가하여 5가 되면 _upouter에서 보이다시피 _downouter함수로 이동하게 된다.

이때 출력은

*

**

***

****

*****

가 되겠고 커서는 맨 마지막 줄인 *****의 오른쪽에 있을 것이다.

 

 

 

 

생각보다 포스팅이 길어지고 있다.. downPyramid에 대해서는 '[assembly] 어셈블리어로 별피라미드 출력하기-(3)'에서 뵙도록 하겠다.