ㅇ예제는 저번에 사용했던 reverse.asm 을 사용하고


1. reverse(스택)

-문자 배열


C언어


int main()

{

char buffer[1024]={0,};

int len = 0;

printf("input:");

gets(buffer);

while (buffer[len] != 0) {

len++;

}

len --;

while(len>=0){

printf("c",buffer[len]);

len--;

}

printf("\n");

return 0;

}


어셈블리 언어


extern printf

extern gets



segment         .data

prompt1         db      'input:',00

prompt2         db      '%c',00

prompt3         db      10,00

prompt4         db      '%s',10,00


segment         .text

global  main


main:

        push    ebp

        mov     ebp,    esp

        sub     esp,    1028


        mov    ecx,    256

        xor     eax,    eax

        lea     edi,    [ebp-1024]

        rep    stosd


        mov     dword [ebp-1028],       0


        push    prompt1

        call      printf

        add     esp,    4


        lea     eax,    [ebp-1024]

        ;mov    eax,    ebp

        ;sub    eax,    1024

        push    eax

        call    gets

        add     esp,    4


while1:

        mov     ebx,    dword [ebp-1028]

        cmp     byte [ebp-1024 +  ebx * 1],     0

        je      .end

        inc     dword [ebp-1028]

        jmp     while1


.end:

        dec     dword [ebp-1028]


while2:

        mov     ebx,    dword [ebp-1028]

        cmp     ebx,    0

        jl      .end


        movzx   eax,    byte [ebp-1024 + ebx * 1]

        push    eax

        push    prompt2

        call    printf

        add     esp,    8


        dec     dword [ebp-1028]

        jmp     while2


.end

        push    prompt3

        call    printf

        add     esp,    4


        xor     eax,    eax


        leave

        ret


stack 에서 변수 공간을 할당할때는 선언된 순서대로 잡아준다

배열의 시작주소는 지정된 크기중 가장 낮은 주소에 위치한다.


처음 부터 확인해 보면 

        push    ebp

        mov     ebp,    esp

        sub     esp,    1028


프롤로그 함수와 변수 2개의 공간을 확보한다.


그림으로 확인해 보면 


len 시작 위치는 dword [ebp-1028]

1024 buffer buffer 시작위치는 byte [ebp-1024] 

saved ebp

saved eip



배열의 시작주소는 지정된 크기중 가장 낮은 주소에 위치한다.


배열이 시작하는 위치를 알아야 불러오는게 가능하다

byte [ebp-1024]

byte [ebp-1023]

byte [ebp-1022]

byte [ebp-1021]





지역변수는 꼭 초기화로 해주어야 한다.

stack은 여러 프로그램이 사용하기 때문에 꼭 초기화 해주어야한다.

 

배열의 크기가 작을때는 

 mov     dword [ebp-1024],       0

 mov     dword [ebp-1020],       0

 ...

이런식으로 초기화 해준다.


       mov    ecx,    256

        xor     eax,    eax

        lea     edi,    [ebp-1024]

        rep    stosd



ecx 카운터

edi 목적지 주소


한칸에 4바이트씩 구성되어 있기 때문에 256 번 반복을 해서 ecx에 256을 대입하고

eax 를 xor을 이용해서 초기화 시켜준다


lea edi, [ebp-1024]

배열의 시작 주소인 [ebp-1024] 를 edi에 저장한다

edi는 목적지 주소이다

rep stosd 반복한다



그렇치 않으면 ESI, EDI:STOS, LORD, REP ... 레지스터를 이용해서 초기화 한다.

extern printf

extern gets



초기화 하는 방법 

char buffer[1024]={0,};


main:

        push    ebp

        mov     ebp,    esp

        sub     esp,    1028


        mov     ecx,    1024

        xor     eax,    eax

        mov     al,     65

        lea     edi,    [ebp-1024]

        rep     stosb


        lea     eax,    [ebp-1024]

        push    eax

        push    prompt4

        call    printf


        add     esp,    8

        leave

        ret



 ;mov    edi,    [ebp-1024] //메모리의 값을 가지고온다.

 lea     edi,    [ebp-1024] //[]있다고 메모리가 아닌 주소의 값을 가지고온다.



mov     dword [ebp-1028],       0

len 변수를 0으로 초기화 시켜준다

        push    prompt1
        call      printf
        add     esp,    4

입력 받는 문장을 출력하고 add로 esp를 돌려준다


lea     eax,    [ebp-1024]
        ;mov    eax,    ebp
        ;sub    eax,    1024
        push    eax
        call    gets
        add     esp,    4

이제 배열에 입력받은 값을 저장해야하는데 
lea로 배열 처음 시작 주소인 [ebp-1024] 로 이동해도 좋고 
아니면 mov로 eax, ebp  기준점으로 이동한후 sub로 1024를 빼주어도 시작 주소이다
call gets 함수를 부르고  add esp, 4 해준다


// 여기 까지가 C 언어 에서 
char buffer[1024]={0,};
int len = 0;
printf("input:");
gets(buffer);

변수 값을 초기화 해주고 배열buffer 에 입력받아 저장해 주었다.

while (buffer[len] != 0) {
len++;
}
len --;

반복문 처리를 해주면

while1:
        mov     ebx,    dword [ebp-1028]
        cmp     byte [ebp-1024 +  ebx * 1],     0
        je      .end
        inc     dword [ebp-1028]
        jmp     while1

.end:
        dec     dword [ebp-1028]


len의 주소인 [ebp-1028] 의 값을 ebx에 저장하고
ebx=0이 저장된다
배열에 abc가 저장되었다고 생각하면 

cmp     byte [ebp-1024 +  ebx * 1],     0

cmp byte [ebp-1024 배열의 시작 주소 + 0 ], 0 
이렇게 되면 배열의 시작 주소와 0을 비교해서 같으면 .end 로 점프한다
배열의 시작이 a 니까 0이 아니라서  아래 문장이 실행된다

        inc     dword [ebp-1028]
        jmp     while1

ebp-1028 즉 len 이 1 증가하고 무조건 분기로 while 로 이동한다

위의 과정을 그림으로 표현하면  



이렇게 표시가 될것이다 

이제 len의 값이 하나씩 늘어서 만약 4가 되면 cmp 비교 값에 의해 .end 로 이동하고 반복문은 종료가 될것이다 


.end:
        dec     dword [ebp-1028]

.end 에서는 len 의 값을 1 감소 시켰다 0나온 값보다 하나 작아야 마지막 값을 가리키는 배열 개수가 출력되기 때문이다

while(len>=0){
printf("c",buffer[len]);
len--;
}

두번째 반복문을 처리해주면 

while2:
        mov     ebx,    dword [ebp-1028]
        cmp     ebx,    0
        jl      .end

        movzx   eax,    byte [ebp-1024 + ebx * 1]
        push    eax
        push    prompt2
        call    printf
        add     esp,    8

        dec     dword [ebp-1028]
        jmp     while2

.end
        push    prompt3
        call    printf
        add     esp,    4



mov     ebx,    dword [ebp-1028]
마지막 배열값인 len 을 ebx에 저장하고
cmp ebx, 0 
jl     .end

len 의 값을 비교해서 0보다 작으면 ,end 로 이동하고  그전에는 이제 배열에 있는 값을 출력해야한다
movzx   eax,    byte [ebp-1024 + ebx * 1]

abc를 대입했다고 가정했으니까 len은 3이 될것이다 ebx에 3을 대입하고 movzx를 수행하면 eax에는 배열 마지막 값인 c가 저장될 것이다.

배열의 값을 끝에서 부터 1개씩 출력한다. 
그리고 esp를 push 한 만큼 돌려주고 len 값을 하나씩 낮춰서 반복문을 진행하고 끝난다



        xor     eax,    eax

        leave
        ret

에필로그 해주고 끝난다 

형식 : MOVZX [OPER1] [OPER2]
의미 : OPER2의 내용을 OPER1에 복사한다. 이때, 확장되고 채워지지 않는 나머지 비트(공간)를 '0'으로 채운다.

위 MOV 같은 경우는 OPER1과 OPER2의 사이즈가 동일하여야지 가능하다. 하지만 이 명령어는 두 OPER 간의 사이즈가 다를 경우 사용된다. 기본적으로, 복사하는 곳의 사이즈가 OPER1의 사이즈가 OPER2의 사이즈보다 커야한다. 때문에, MOVZ'X'의 'X'가 확장의 의미이다.



2. 배열의 총합(스택)

-숫자 배열

-int sum(int *array, int len);



1), C 언어

 

           [ebp+8]    [ebp+12]

int sum(int *array, int len)

{

int sum=0;      [ebp-4]

len --;

for( ; len > 0; len--){

sum = sum + array[len];

}

return sum;

}


int main( )

{


int array[ ]={10,20,30,40,50};

int result = 0;

result = sum( array,5 );

printf("sum:%d\n",result);


return 0;


}



2). 어셈블리


extern printf


segment         .data

prompt          db      'sum:%d',10,00



segment         .text

global  main


sum:

        push    ebp

        mov     ebp,    esp

        sub     esp,    4


        mov     dword [ebp-4],  0

        dec     dword [ebp+12]

for:

        mov     ebx,    dword [ebp+12]

        cmp     ebx,    0

        jl      end

        mov     edx,    dword [ebp+8]

        mov     eax,    [ edx + ebx *4 ]

        add     dword [ebp-4],eax

        dec     dword [ebp+12]

        jmp     for


end:


        mov     eax,    dword [ebp-4]

        leave

        ret


main:

        push    ebp

        mov     ebp,    esp

        sub     esp,    24


        mov     dword [ebp-4], 10

        mov     dword [ebp-8], 20

        mov     dword [ebp-12], 30

        mov     dword [ebp-16], 40

        mov     dword [ebp-20], 50

        mov     dword [ebp-24], 0


        push 5

        lea     eax,    [ebp-20]

        push    eax

        call    sum

        add     esp,    8


        mov     dword [ebp-24],eax

        push    dword [ebp-24]

        push    prompt

        call    printf

        add     esp,    8


        xor     eax,    eax



        leave

        ret



이것도 하나하나 분석해보면 


main:

        push    ebp

        mov     ebp,    esp

        sub     esp,    24


배열 20바이트에 result 4 바이트해서 총 24 바이트 값을 확보한다

배열이 초기화가 되어 있는 경우에는 반복문으로 입력하는게 아니라 하나하나 입력해준다 

 


   mov     dword [ebp-4], 10

        mov     dword [ebp-8], 20

        mov     dword [ebp-12], 30

        mov     dword [ebp-16], 40

        mov     dword [ebp-20], 50

        mov     dword [ebp-24], 0


배열의 시작 위치는 ebp-20 이고 

시작부터 반대로 대입시켜 준다 50,40,30,20,10 순서대로 초기화 시켜주고

dword [ebp-24] result 값을 0으로 초기화 시켜준다


이제 sum 함수를 출력하는데  

result = sum( array,5 );


    push 5

        lea     eax,    [ebp-20]

        push    eax


뒤에서 부터 입력하고 call 해주어야 한다 

우선 숫자 값인 5를 push 하고 array의 시작 주소인 [ebp-20] 를 eax에 넣고 push 시켜준다.


        call    sum

        add     esp,    8

이제 sum 함수를 호출 시켜주고 push push 에 대한 esp 메모리를 회수한다

call 함수는 


push eip +jmp sum = call sum

복귀 주소인 eip를 저장하고 jmp sum 하는 의미를 가지고 있다





   

sum:

        push    ebp

        mov     ebp,    esp

        sub     esp,    4


        mov     dword [ebp-4],  0

        dec     dword [ebp+12]

for:

        mov     ebx,    dword [ebp+12]

        cmp     ebx,    0

        jl      end

        mov     edx,    dword [ebp+8]

        mov     eax,    [ edx + ebx *4 ]

        add     dword [ebp-4],eax

        dec     dword [ebp+12]

        jmp     for


end:


        mov     eax,    dword [ebp-4]

        leave

        ret



sum 함수로 와서 보면 

int sum=0; 


지역 변수가 있으니까 프롤로그 하고 sub 4 로 공간을 확보 했다


 mov     dword [ebp-4],  0

 dec     dword [ebp+12]


sum 변수 값을 초기화 시켜주고 






push 5 값인 ebp+12 를 1 감소 시켜준다. 



mov   ebx,    dword [ebp+12]

        cmp     ebx,    0

        jl      end

        mov     edx,    dword [ebp+8]

        mov     eax,    [ edx + ebx *4 ]

        add     dword [ebp-4],eax

        dec     dword [ebp+12]

        jmp     for




이제 위에 했던거 처럼 반복문을 수행하고 main으로 리턴 시켜준다 












+ Recent posts