이 영역을 누르면 첫 페이지로 이동
caputdraconis 블로그의 첫 페이지로 이동

caputdraconis

페이지 맨 위로 올라가기

caputdraconis

네트워크 관점에서의 클라우드 컴퓨팅을 공부하는 중입니다 :)

지옥방 스터디 포너블

  • 2020.04.09 21:15
  • Security/지옥방 스터디
글 작성자: caputdraconis
반응형

실습환경 : Ubuntu 18.04.4, Nasm

 

어셈블리어 :기계어와 1 대 1로 대응되는 명령어 체계를 가진 컴퓨터 프로그래밍 언어

어셈블리어 문법은 두가지가 있다 => 윈도우 사용 : Intel 문법 / 리눅스 사용 : AT&T 문법

mnemonic이 여기서는 opcode!

opcode 명령어 또는 연산자의 의미를 갖는다.

operand 데이터 또는 피연산자의 의미를 갖는다

opcode가 '더하라'이고 operand가 3,4라면 3과 4라는 더하라는 명령이라고 이해할 수 있다.

 

메모리

메모리의 기본 단위는 1 byte이다. 메모리에서 각 byte는 메모리 주소라는 고유 숫자가 붙어있다.

메모리 단위로는 word = 2 바이트, double word = 4 바이트, quad word = 8 바이트, paragraph = 16 바이트 . 메모리의 모든 데이터는 숫자로 되어 있다.

 

어멋!? 레지스터가 뭐야~?

레지스터는 cpu안에 존재하면서 연산을 수행하는 녀석이다. 컴퓨터 안에서 제일 빠른 연산 속도를 가진 녀석이기 때문에 이 친구가 도맡아한다. 근데 용량이 작아서 레지스터에 프로그램을 받아서 연산을 돌릴 수는 없다. 그래서!!?? 프로그램은 ram에서 맡고 ram이 프로그램을 돌릴 때 cput의 레지스터와 교류를 하며 연산을 수행한다.

 

범용 레지스터 우리가 사용 가능한 레지스터이며, 산술/논리 연산, 오퍼랜드(피연산자)를 저장, 포인터의 역할로 사용한다

EAX(AX, AH, AL) - 누적연산기, 곱셈과 나눗셈 연산에서 자동으로 사용. A는 Accumulate의 A 같다.

EBX(BX, BH, BL) - 베이스 레지스터, 특정 주소를 지정한다. B는 Base의 B 같다.

ECX(CX, CH, CL) - 수를 셈, 자동으로 루프 카운터 된다. (반복적인 명령 수행시) C는 Count의 C 같다.

EDX(DX,DHT, DL) - 데이터 레지스터, 입출력 연산에서 반드시 간접 주소 지정에 사용. Data의 D.

EBP(BP) - 베이스 포인터, 스택의 데이터에 접근하기 위해 사용. Base Pointer의 BP

ESP(SP) - 스택 포인터, 현재까지 사용된 스택의 위치를 저장, 스택 최상부의 오프셋을 가리킴. Stack Pointer의 SP

 

ESI(SI) - 읽기 인덱스, 문자열 전송이나 비교에서 사용되는데 주로 소스 문자열의 오프셋을 가리킴. Source Index의 SI

EDI(DI) - 쓰기 인덱스 Destination의 D

EIP(Instruction Pointer) - 명령어 포인터 레지스터, 실행할 다음 명령어의 주소를 포함.

EFLAGS - CPU의 동작을 제어하거나 CPU 연산의 결과를 반영

 

세그먼트 레지스터

CS(Code Segment) - 코드를 저장하는 메모리 블록

DS(Data Segment) - 데이터를 저장하는 메모리 블록

EX(Extra Segment) - 비디오와 관련된 것을 위해 사용됨

SS(Stack Segment) - 루틴으로부터 리턴 어드레스를 저장하기 위해 프로세서에 의해 사용되는 레지스터

 

인덱스 레지스터

SI(Source Index) - 문자열/배열의 소스를 지정하기 위해 사용됨.

DI(Destination Index) - 문자열/배열의 목적지를 지정하기 위해 사용됨.

IP(Instruction Pointer) - 다음 명령의 주소를 지정함! 그래서 그 값을 직접적으로 변경 불가능.

 

스택 레지스터

BP(Base Pointer) - 스택 오퍼레이션을 위해 SP와 연결되어 사용됨

SP(Stack Pointer)

 

특별한 목적을 위한 레지스터.

IP(Instruction Pointer) - 실행된 명령의 offset을 가지고 있다.

Flag - 분기(branching)를 위해 사용. 플래그 레지스터는 크기가 1 바이트이다.

 

MOV 연산자

형식은 MOV 목적지, 값 이다. 

MOV AX, 56h

AX를 56h와 같게 한다.

MOV AX,BX

AX에 BX의 값을 복사 붙여넣기 한다.

 

XCHG 연산자

단순히 두 레지스터를 서로 바꾸는 연산자. exchange에서 나온 XCHG~

형식은 XCHG 레지스터1, 레지스터2 이다.

위의 코드에서는 DX에 56h라는 값을, AX에 3Fh라는 값을 복사하고, XCHG 연산자를 사용하여 DX와 AX의 값을 서로 바꿨다.

즉 위의 예에서는 AX는 56h, DX는 3Fh의 값을 갖고있다.

**주의** 8비트 (h/1) 레지스터와 16비트 (x)와 교환하지 않도록 해야한다. 이를 어기는 코드는 무.효.

 

INC와 DEC

INC는 increase, DEC는 decrease에서 나온 것이당.

DX의 값은 56h로 설정되었고 그 밑에서 INC 연산자를 사용해 DX의 값을 증가시켰다. DX는 51h가 되었다..!

DX의 값은 50h로 설정되었고 그 밑에 DEC 연산자를 사용해 DX의 값을 감소시켰다. DX는 4Fh가 되었다.

 

스택 조작 - POP PUSH

*형식 : POP 레지스터 PUSH 레지스터 *

나중에 사용하기 위해 스택에 있는 A의 값을 일시적으로 저장하고자 한다면 PUSH AX를 사용한다.

그리고 원래의 값을 회복하고자 한다면 POP AX을 사용하면 된당.

스택은 단지 16비트 레지스터만 받아들인다는 것 주목!

위의 코드를 실행했을때 AX, BX의 값은 몇일까~?

1.AX의 값을 51h로 설정, BX의 값을 4Fh로 설정

2.XCHG 연산자를 이용하여 둘의 값을 바꾼다.

3. PUSH AX로 현재 AX의 값을 일시적으로 저장한다. 그리고 MOV 연산자 AX의 값이 34h로 설정.

4.POP BX 로 BX의 원래 값을 회복. 즉 BX = 4Fh

5.PUSH BX로 BX의 값을 일시적으로 저장. 지금 BX = 4Fh

6. POP AX를 하면 AX = 34h 이전의 값을 회복한다. 즉 AX = 4Fh!!

그럼 BX는 4Fh, AX는 4Fh이다.

 

 

** 수학 연산자 조작**

ADD

형식은 ADD 레지스터1, 레지스터2 또는 ADD 레지스터, 값

ADD AX, BX => AX에 BX를 더하고, 그 값을 AX에 저장한다.

 

 

SUB

형식은 SUB 레지스터1, 레지스터2 또는 SUB 레지스터, 값

그냥 딱 ADD랑 반대

 

 

MUL

형식 : MUL 값(or 레지스터)

하나의 operand만 필요. 주어진 레지스터를 AX 또는 AH로 곱하기를 원한다고 추정하기 때문

 

 

 

DIV

형식 : MUL과 같다!

원리도 같다!

 

 

Bit 연산자! - 두 값을 bit 단위로 비교를 하는 것.

AND 

-형식 : AND 레지스터1, 레지스터2 또는 AND 레지스터, 값

-두 어포랜드가 1일 경우만 1을 리턴,

-ex)

5h => 101b

6h => 110b

      100b  <= AX =4h

-둘다 1일때만!!!!

 

 

OR

-형식 : OR 레지스터1, 레지스터2 또는 OR 레지스터, 값

-하나라도 1이면 1 리턴

AX = 5h =>101b

BX = 6h =>110b

             111b  <= AX = 7h

 

 

XOR

-형식 : XOR 레지스터1, 레지스터2 또는 XOR 레지스터, 값

-XOR는 오퍼랜드가 만약 어느 하나가 1이면 1을 리턴하고, 둘다 1이면 0을 리턴.

AX = 5h => 101b

BX = 6h => 110b

              011b  <= 3h

 

 

NOT

-형식 : NOT 레지스터 또는 NOT 값

-이건 operand 하나 주어짐. 주어진 operand의 비트값을 반전시켜놓는다. 101b면 NOT 101b => 010b


** 여기서부터 순서가 이상해질 예정 **

section .data 에서 data란!?: 프로그램에서 사용되는 문자열같은 데이터를 저장할 수 있는 공간을 의미

section .text 에서 text란!?: 위에서 아래로 차례대로 내려가는 소스코드 그 자체를 의미

_start : main함수보다 먼저 실행되는 함수!!

Hello World를 출력하는 프로그램

<레지스터의 용도와 시스템 콜 이해하기>

하나의 segment 내부 구조

stack : 함수에 대한 정보들 포함, 함수 내의 지역변수도 포함!

- ex) int sum(a,b) { } 에서 a,b가 지역변수

Heap : 동적으로 할당된 데이터들이 정의되는 곳 malloc()사용

BSS (uninitialized) : 프로그램에서 사용될 변수들이 실제로 위치하게 되는 영역 -> 초기화 x    ex) input 1 resd 1

Data(initialized) : 초기화가 된 변수들이 위치하는 영역    ex) msg db "늦어서 죄송합니당,,",20,0

Text : 우리가 작성한 소스코드가 들어가는 영역

 

 

아까 만들었던 Helloworld 출력 프로그램에서 section .data에서 작성된 msg db "Hello World"는 위의 그림에서 Data 영역에 "Hello World"라는 문자열을 저장하게 된다.

 

 

데이터 지시어

데이터 지시어는 데이터 세그먼트에서 메모리상의 공간을 정의하는 데 사용된다. NASM에서는 메모리 공간을 정의하는 2가지 방법을 제공하는데, 매모리 공간만을 정의하는 resx 계열 지시어고, 다른 하나는 메모리 공간과 초기 값을 지정하는 dx 계열의 지시어이다. resx와 dx의 x에는 다음과 같은 문자가 들어갈 수 있다.

문자 dx resx 단위(Unit) another name
b db resb 1 byte

바이트

w dw resw 2 byte 워드
d dd resd 4 byte 더블 워드(dword)
q dq resq 8 byte 쿼드워드(qword)
t dt rest 10 byte 텐 바이트(tbyte)

아까 만들었던 Hello World를 출력하는 프로그램에서 작성했던 Section .data의 " msg db 'Hello World!', 10,0  "에서의 db가 바로 위 표의 데이터 지시어 db다.

 

이와 같이 사용한다잉~

 

<에코 프로그램 만들기>

xor rax, rax rax와 rax에게 비트연산자 XOR을 사용했다. XOR은 서로 비트가 다른 값이여야 1을 뿜뿜하는데 같은 값끼리는 비트가 다를 수 없기 때문에 무조건 0이 나온다.
mov rbx, rax rbx에 rax의 값을 할당한다. 0!
mov rcx, rax rcx에 rax의 값을 할당한다. 0!
mov rdx, rax rdx에 rax의 값을 할당한다. 0!
sub rsp, 64 rsp의 값에서 64를 뺀다. 64 바이트 크기의 데이터 공간을 마련해줌으로써 입력받은 문자열이 저장될 공간을 형성해준다.
mov rdi, 0

파일 디스크럽터는 무언가를 읽을때는 0, 쓰는 경우에는 1

여긴 읽는 경우이니 0!

mov rsi, rsp rsi에 rsp의 값을 할당한다. rsi는 입력받은 문자열을 가리킨다. rsp는 문자열을 저장할 공간의 첫번째 주소를 가리키기에 이 명령이 실행되면 주어진 공간에 문자열이 저장되게 된다.
mov rdx, 63 rdx에 63의 값을 할당한다. rdx는 입력받을 데이터의 크기가 저장된다.
syscall 띠드콜
mov rax, 1 rax에 1을 할당시킨다. rax가 1일때 syscall은 sys_write 역할을 한다. 즉 그냥 출력ㅎㅎ
mov rdi, 1 파일 디스크럽터인 rdi에 1이 할당되었으니 무언가를 쓰는 경우다!
mov rsi, rsp rsi에 rsp를 
mov rdx, 63 rdx에 63을 할당한다. rdx에는 출력할 데이터의 크기가 주어진다.
syscall 띠드콜
mov rax, 60

rax에 60이!? 이것은 sys_exit을 뜻한다,,즉 종료닷

syscall 띠드콜

echo 프로그램 구동 캡처!

 

 

 

LEA : A의 값을 B의 값으로 연산을 포함하여 복사합니다. LEA EAX, [EAX+1000]은 EAX에 1000을 더한 값을 EAX에 저장한다.

JMP : 특정한 위치로 건너 뛰어 코드를 실행합니다. JMP A라고 하면 A의 위치로 뛰어서 코드가 실행됩니다. JA, JB, JE 등의 명령어는 두 인자를 받아서 비교한 뒤에 결과에 따라서 다른 방향으로 점프할 수 있습니당.

CALL : 함수를 호출했다가 다시 원래 위치로 돌아올 때 사용합니다. JMP와 다른 점은 실행한 뒤에 끝나게 되면 RET에 저장하고 다시 원래 상태로 돌아온다는 점이다.

NOP : 아무것도 하기싫다. 아무것도 하고 있지 않은데 더더욱 아무것도 하기 싫다. 1 바이트의 빈 공간 차지

RET : 현재 함수가 끝난 뒤에 돌아갈 주소를 지정하기 위해 사용한다.

LEAVE : 현재까지의 메모리 스택을 비우고 EBP를 자신을 호출한 메모리 주소로 채웁니다. 실행 중인 함수를 종료하기 위해 정리하는 작업에 사용된다!

 

<반복문>

mov rax, 1 rax가 1이에요. 출력하겠다 이 말이죠!?
mov rdi, 1 rdi가 1이에요! 쓰겠다 이거죠!?
mov rsi, msg rsi가 msg에요. 출력할 문자열이 'A'라는거죠!?
mov rdx, 1 rdx에 1을 할당해요
mov r10, 1

r10에 1을 할당해요

cmp r10, 100 r10이랑 100이랑 비교를 해요
je done 같다면!? 그만둬요!
syscall 띠드콜~
mov rax, 1 rax에 1을 저장한다잉~ 이건 출력하겠다 이거다잉~
inc r10 r10의 값을 +1한다잉~
jmp again again으로 다시 가버려
mov rax, 60 sys_exit 프로그램 종료~
mov rdi, 0 오류 없음!

위의 반복문 코드 실행 결과

*어느 문서에서 봤는지는 기억이 안나지만.. 함수를 만들고 인자를 줄때는 정해진 인자 순서의 역순으로 스택에 push한다.

gdb를 이용하여 _start에서 브레이크를 걸어놨다.

레지스터들의 상황을 볼 수 있다. 하 졸리다.

RSP 특정 메모리를 가리키고 있다.

RIP 어떤 명령을 실행할지 가리키고 있다.

현재 _start에서 브레이크가 걸려있는걸 알 수 있다. 그 오른쪽에는 어셈블리어로 실행되는 명령어가 적혀있다.

터미널에 ni를 한 번 치면 하나의 단계를 넘어간다. 어.. 그니깐! 다음 명령어를 실행하려면 ni!

스택의 상황도 볼 수 있어요 엄청나죠?? 봐도 뭔지는 몰라요오 ㅎㅎㅎㅎㅎ

 

 

어셈블리 함수의 정의와 선언

== 어셈블리 ==

TEST PROC NEAR32
    push ebp
    mov ebp,esp
    함수 내용부~


    mov esp, ebp
    pop ebp
    ret
TEST ENDP

== C언어 ==

void TEST(void)
{
함수 내용부~~

}

위의 두 코드는 같은 기능을 한다. TEST라는 이름의 함수를 만들어준다.

메인은 어셈블리 함수이니 자세히 살펴보면

push ebp

mov ebp,esp 는 함수의 Entry Code라 하고 함수 시작부 주소의 설정과 호출되기 전 함수의 시작부 등을 설정해주는 기능을 한다. 꼭! 있어야하는 코드. Entry Code가 있으면 ? Exit Code도 있다잉

mov esp, ebp

pop ebp

ret Entry Code에서 변경 되었던 EBP를 다시 호출되기 전 값으로 복구 시키고 리턴 어드레스로 돌아가게 하는 역할을 수행


strlen, strcat, strcat 함수를 어셈블리어로 구현한다. 구현해보며 알게되는 사실은 여기다가 게시한다.

1. strcpy

strcpy(char* dest, const char* src, size_t_count) 의 형식으로 사용된다. 복사된 값이 저장될 문자열, 복사될 문자열, 복사할 문자의 개수가 주어진다. 흐음 이걸 어떻게 할까. 일단!? 문자열을 받아서,,그걸 저장을 해야겠찌,..? 그리고 데스티네이션 문자열에 그걸 대입해야겠찌,,?

size_t_count라는 이름으로 복사 붙여넣기할 크기를 알려주겠지..?

1)그럼! 문자열을 입력 받아보자. 도대체 문자열은 어떻게 입력받는걸까..?

친한 친구 한명에게 물어보고자 한다. 하씨 안나온다.

친구가 많아서 다행이다.

어,,,? 아닛???? 저것이 보이는가? 내 눈이 잘못됐나? 어..?

눈 좀 비비고

엇,,? 이것은? strlen!!????

 

 

 

2)strlen

문자열을 입력받고 문자의 갯수를 출력하는 함수다

주제를 바꾸고 저 글을 읽어보니 nasm 기준이 아니여서 모르는게 막 나온다. 큰일났다

하 큰일났다. 그래도 그나마 strlen이 만만하다. 해보자

 

예전에 구글링을 해봤을때 알게된 구현방식은

문자 하나하나를 null문자와 비교하며 반복문을 돌리는 것이였다.

해보자..! 프로그램의 초반은 위에서 만들었던 에코 프로그램을 그대로 썻다.

rax,rbx,rcx,rdx를 모두 0으로 초기화 해준다.

xor rax,rax는 rax를 0으로 초기화 시키는 것을 의미한다. 왜냐면!?

xor은 비트단위로 비교를 실행하는데, 1 + 0 =>1 이고 나머지는 다 0이다. 즉 같은 값끼리 비교하면 1 + 1 => 0, 0 + 0 =>0이므로

모두 0!

이제 한 문자씩 널 문자와 비교해야한다. 어!!?? 비교? 비교를 어떻게 하지?

친한 친구다

cmp

비교 명령어다. 형식은 cmp opnd1, opnd2

cmp 명령은 내부적으로 opnd1과 opnd2를 빼는 과정을 거친다고 한다. 즉 플래그 레지스터의 값 변경이 발생한다고 한다!!!

이거 어뜩해!? 아 우린 문자열의 길이만 출력하면 되니 상관이 없다ㅎㅎ

 

cmp 로 비교를 해서 널 문자가 아니라면 rcx ( 반복문에서 갯수 세는 용도 )에 1씩 더하고 다음 문자를 똑같이 비교하게 하면 되겠다.

만약 널문자라면 반복문을 중단하고 그때까지의 rcx를 출력하면 되겠찌..?요?

jz 반복문을 빠져나오려면

아까 거기다. rsp에서 64를 빼줌으로써 입력되는 문자열을 저장할 공간을 만들어준다.

rax가 0이기 때문에 syscall을 실행하면 sys_read가 실행이 되는데 그럴려면

rdi에 입력받겠다는 숫자 0. rsi에 할당된 공간의 첫주소값. rdx에 입력받을 크기를 할당해줘야한다.

syscall을 하면 문자열을 입력받는다.

그럼 받은 다음 비교를 시작해봅시다.

rsi와 byte 0를 비교하고 있다. byte 0가 널문자라는 소문을 들었기 때문이다. rsi가 byte 0라면, 즉 널문자라면 _null_e_da (널이다!) 로 이동하며 반복문은 끝나게 된다. 그런데 여기서 조금 살짝 궁금한게,, 위에서 rsi에 주소값을 할당해놓지 않았나..? 근데 그 주소값으로 비교를 해도 되나 싶다. 모르겠다 일단 넘어가자

비교를 했을때 널문자가 아니라면 rcx (널문자 이전의 문자 개수)에 inc를 사용해 1을 더해준다

현재의 rsi는 비교를 했으니 다음 rsi를 비교하기 위해 rsi도 inc로 1 더해준다.

그리고 다시? 다음 문자를 비교하게 하기 위해

쩜프!!!!!!!!!!!!!!!! _strlen으로 다시 복귀

널문자를 만났을때 오게되는 그 곳,,

rcx를 출력하기 위해 rax도 1로 바꾸고 준비중

이대로 간다..

채널 고정

파일 이름이 모두 strcpy로 되어있는 이유는 위에서 볼 수 있듯이 strcpy에서 strlen으로 급격히 꺾었기 때문이다.

프로그램이 끝나지 않는다..왜라고 생각하는가..?

 

 

 

나는 잘 모르겠다. 

 

 

 

그 시행착오 제가 겪어보겠습니다.

 

프로그램이 종료되지 않는 버그를 고치기 전에 아까 거슬렸던 rsi와 byte 0를 비교하는 부분을 보자.

내 짧은 지식으로 생각을 해봤는데,,  아무리 생각해봐도 rsi는 주소값이다. 주소값을 널문자와 비교하는게 어떤 의미가 있을까

없어!!!!!!  그 주소값에 할당된 값이랑 비교하지 않는 이상 의미가 없을것 같다는 결론이 나온다.

c언어에서는 주소값에  * (포인터)를 붙이면 그 주소에 저장된 값을 사용할 수 있지 않은가?

 

 

그게 어셈블리어에서는 뭘까!? 수소문을 시작해봤다.

자기 일처럼 정말 팔 벗고 나서주는 좋은 사람들이 많다. 이 세상은 아직 좋은 세상이라고 생각된다.

[]

그 안에 있는 값을 주소로 인식하여, 그 주소가 가리키는 곳을 찾아가라는 의미

 

와 그럼 아까의 코드를 수정해보자

이대로 가보자..!!!

 

 

 

뭘까 이 친구는.

차라리 영어로 말해줘 친구야ㅎㅎㅎㅎ

 

 

 

느낌이 온다.. segmentation error 라면,,

나를 놀리는걸까?

 

분할된 부분중 어느 하나가 잘못되었다는걸까..?

아까 맘에 걸렸던 부분이 하나 더 있다. 그걸 해결해보자

 

'널이다' 부분이다.

이제 다 정리하고 출력하는 부분인데, rsi가 출력 버퍼?니깐 거기다가 그동안 더해온 rcx 값을 복사해 출력하려는 의도였다.

근데 아까도 봤다싶이 rsi는 주소값이였다. 그럼 대괄호를 씌워볼까..?

 

 

요즘 욕을 참 많이 하는거같다.. 집 밖에는 벚꽃도 많이 피었으니 그걸 보며 마음의 안정을 되찾는게 중요할거같다.

이유가 뭘까

 

 

후우,,다시 시작해보자 

 

 

 

 

 

내준 과제를 이해도 못하냐고 욕하시는 장면은 뺏습니당ㅠㅠㅠㅠ

아자아자 화이팅!

 

 


시작!

 

 

segment는 소스 코드에 영역별로 메모리를 정의하고 싶을 때 사용하는 어셈블러 지시어, segment 다음에 영역을 넘겨서 해당 영역데 메모리를 정의할 수 있도록 한다.

s db 'I love sak!',10,0

s 는 문자열 이름

db는 byte 형식의 데이터를 의미. 즉 s는 byte 배열로 정의된 레이블이다.

문자열 뒤에 콤마(,) 이후 10과 0 이라는 숫자가 나왔는데, 여기서 10은 개행 문자의 ASCII 코드 값이고, 0은 널문자(\0) 로 문자열을 끝내기 위해 0을 마지막에 넣었다.

 

 

 

 

위에서 segment에 대해서 잠깐 알아봤다. segment .text는 해당 지시어 다음에 등장하는 모든 소스가 코드 세그먼트에 대한 것이라고 어셈블러에게 전달하는 어셈블러 지시어다.

 

global main _main 레이블이 전역에 선언된 레이블임을 의미하는 어셈블러 지시어다. 기본적으로 어셈블리 언어의 레이블들은 모두 내부 선언되어 있기 때문에 global을 붙여주지 않으면 다른 파일에서 이 레이블에 접근하는 것이 불가능, 즉 global은 다른 파일에서 레이블에 접근할 수 있도록 만들어준당. 발이 넓은 친구 ㅎㅎ

 

_main   _min 프로시저를 정의한다. 프로스저의 정의는 어셈블리에서 레이블의 정의와 같다!  아마도 procedure..?

 

맞구만

 

 

 

 

 

 

 

 

문자열을 입력받아서 하던 아까와 달리 .bss 세그먼트에다가 s라는 이름의 "I love Sak!"+ 개행문자+ 널문자를 선언해놓았다.

이 문자열의 길이를 반환하는 strlen 프로그램을 만들어보고자 한다.

 

이는 오류가 난다,,

_start:

하 시작이다

xor rax,rax 위에서도 사용했던 초기화 방법이다.
mov rbx,rax
mov rcx,rax
mov rdx,rax
mov esi, s 주소값 esi 에 문자열 s를 대입해봤다, 여기서 문제가 있을까..?
_strlen: 본격적으로 문자열의 길이를 세는 구간이다.
cmp [esi], byte 0 byte 0 는 널문자를 의미, 주소값 esi에 있는
JE _stop 만약 널문자가 발견된다면 _stop으로 이동
inc rbx 위에서 널문자가 발견되지 않았다면 문자열 길이를 카운트하는 rbx에 1을 더해준다
inc esi 이번 주소값에 있는 문자는 비교가 끝났으니 다음 비교를 위해 esi도 1을 더해준다.
jmp _strlen 다시 비교하기 위해 다시 위로 뿌슝
_stop: 널문자가 발견되었을때 오는 곳이다. 출력을 준비한다.
mov rax,1 sys_write를 준비중
mov rdi,1
mov rdx,3
mov rsi, rbx

rsi 주소값에 아까 카운트한 값인 rbx를 대입해보았다.

여기도 뭔가 이상하다,,

syscall 간닷

 

 

 

익숙한 친구다. ㅎㅎ

 

 

아까 찝찝했던 부분들을 고쳐보자

 

위 표의 노란색 배경의 글씨 부분을 고쳐보았다.

놀랍게도 방금 전의 에러와 다른 친구다 ㅎㅎㅎㅎ다른걸 고쳐보장

 

 

 

 

 

 

위 표에서 문제가 있을까?라고 생각했던 부분이다.

내 생각은 이랬다. esi는 주소값인데 거기다가 문자열을 할당하면 오류가 나지 않을까?

mov [esi],s

그래서 위와 같이 바꿔보았다.

 

 

operation size not specified??? 

구글에 검색해보니 프로세서는 메모리에서 메모리로 이동시키는 instruction이 없어서 하나를 거쳐서 가야된다는 뜻으로 추정되는 영어들이 나타났다. 그래서 

 

 

쓰지 않는 rdx 에 문자열 s를 대입하고 그걸 다시 esi에 대입하니 오류가 나지 않았다!!!!! 그래서 나는 

'이거 진짜 되는거 아니야..?'라는 헛된 꿈과 함께 마지막

./strlenex

를 터미널에 입력했다. 결과는

 

ㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎ

 

 

진짜 뭐가 문젤까?

 

 

 

 

 

코드에 혁명을 일으켜봤다.

결과는 똑같았다. segmentation fault(core dumped)

 

코드를 바꾸기 전에 저게 뭐하는 새끼인지부터 알아봐야 할 것 같다.

 

뭐 cpu에게 프로그램이 종료되었음을 알려주지 않았다~뭐 이런 얘기가 많아서 sys_exit을 포함해봤다.

 

 

이를 실행해보니!?

출력 없이 그냥 끝나버린다. 흐으으음 출력에 문제가 있는 것인가

출력 부분을 한 번 살펴보자

 

원래는 rax에 1을 넣어서 sys_write를 사용했었지만 오류가 계속 나서 위와 같이 바꿨었다. 혹시 이것때문은 아닐까 하는 희망을 가져보고 원래대로 고쳐보겠당!

수정 후의 코드, 위의 코드에서 mov [rsi],ecx 부분은 mov [rsi], rcx로 고쳤다

 

위와 같이 바꾸고 다시 실행을 해보니

공백이 출력된다. 어..?

카운트가 되지 않은건가..? 

 

 

 

 

 

오류를 잡았다,,내가 하도 오류메시지 좀 안뜨게 해달라고 빌어서 그런지 오류메세지는 뜨지 않는다.

대신 아무것도 뜨지 않는다. 

생각을 해보면 현재 rcx는 0으로 초기화 되어있다. 출력이 정상적으로 이루어졌다면 0이라도 출력이 되었어야 하는데, 0도 출력이 되지 않는걸 보니!? 출력에 문제가 있다. 내가 고치고 만다. 1시간 타임어택 시작

 

 

진전이라고 봐야할까..? 26분이 지난 지금 세미콜론(;)을 출력하는데 성공했다.

도대체 저 세미콜론은 어디서 나온걸까..?

엄청난 수정을 거쳐 <를 출력해냈다. 

 

<지금까지의 strlenex_r.asm>

_____________________________________________도저히 해결될거 같지 않아 재빠르게 다른 strlen 코드를 가져와보겠당.__________________________________

 

 

_strlen:

  push  rcx           
  xor   rcx, rcx

_strlen_next:

  cmp   [rdi], byte 0  
  jz    _strlen_null 

  inc   rcx       
  inc   rdi            
  jmp   _strlen_next  

_strlen_null:

  mov   rax, rcx    

  pop   rcx           
  ret             
_strlen strlen을 시작!
push rcx 현재 저장되어 있는 rcx값을 push해놓음으로써 모두 다 끝난후 원래의 rcx값을 이용할 수 있게 한다
xor rcx,rcx rcx를 0으로 초기화
_strlen_next: 본격적으로 반복(loop) 되는 곳이다.
cmp [rdi],byte 0 문자열이 저장되어 있는 주소인 rdi에 저장된 값과 byte 0(널 문자)와 비교를 진행한다
jz _strlen_null 만약 비교를 진행하면서 널문자를 발견한다면 _strlen_null로 이동한다.
inc rcx 비교를 진행하며 널문자가 발견되지 않았다면 아직 문자열인 것이니, 문자열의 길이를 세는 레지스터인 rcx에 1을 더해준다
inc rdi 문자열의 주소값을 저장하고 있는 rdi에 1을 더해줌으로써 다음 문자를 비교할 준비를 한당
jmp _strlen_next 다시 _strlen_next로 복귀함으로서 비교 진행
mov rax,rcx 문자열의 길이를 리턴하기 위해 rax에 문자열의 길이를 갖고있는 rcx의 값을 대입시킨다
pop rcx 아까 초반부에 push 시켜놨던 rcx값을 pop으로 되찾아온다.
ret 문자열의 길이를 반환한다.

위의 풀이가 제가 예상하고 원했던 흐름인데,,내 코드는 왜 정상작동을 거부하져

 

 

 

section .data
	msg1 db 'destination string : ',0
    msg2 db 'source string : ',0
    
section .bss
	destination_string resb 1024
    source_string resb 1024
    
section .text
	global main

main:
	xor rax,rax
   	xor rsi,rsi
    xor rdi,rdi
    xor rbx,rbx
    xor rdx,rdx
    
    mov rax,1
    mov rdi,1
    mov rsi,msg1
    mov rdx,22
    syscall
    
    mov rax,0
    mov rdi,0
    mov rsi,destination_string
    mov rdx,1024
    syscall
    
    mov rax,1
    mov rdi,1
    mov rsi,msg2
    mov rdx,17
    syscall
    
    mov rax,0
    mov rdi,0
    mov rsi, source_string
    mov rdx,1024
    syscall
    
    mov rdi,destination_string
    mov rsi, source_string
    call _strcat
    
    mov rax,1
    mov rdi,1
    mov rsi,destination_string
    mov rdx,1024
    syscall
    
    mov rax,60
    mov rdi,0
    syscall
section data 초기화된 데이터들을 저장하는 data 섹션에 출력할 문자열들을 저장해놓았다.
msg1 db 'destination string : ',0
msg2 db 'source string : ',0 
section .bss 초기화 되지 않은 데이터들을 저장하는 bss 섹션에 이음을 받을 문자열과 이을 문자열을 입력받는 메모리를 할당해놨다
destination_string resb 1024
source_string resb 1024
main: 메인은 문자열을 출력하고 입력받고 하는거 뿐이여서 설명 생략.
_strcat:
	push rbp	
    mov rbp,rsp
    xor r9,r9
    xor r10,r10
    xor rax,rax
    jmp _find_memory
_find_memory:
	mov al,[destination_string+r10]
    cmp al,10
    je _write_data
    add r10,1
    jmp _find_memory
_write_data:
	xor rax,rax
    mov al,[rsi+r9]
    mov [destination_string+r10],al
    cmp al,0
    je _strcat_end
    inc r9
    inc r10
    jmp _write_data
_strcat_end:
	leave
    ret
_strcat: 본격적인 strcat 구현 시작
push rbp rbp에 저장되어 있는 값을 스택에 저장
mov rbp,rsp rbp에 rsp의 값을 대입
xor r9,r9 r9 0으로 초기화
xor r10,r10 r10 0으로 초기화
xor rax,rax rax 0으로 초기화
jmp _find_memory _find_memory로 이동
_find_memory:  
mov al,[destination_String+r10] al에 이음을 받을..?당할..? 문자열의 주소에 r10을 더한 그 주소에 저장되어 있는 값을 대입한다.
cmp al,10 al과 10을 비교한다. 10은 개행의 의미
je _write_data 개행 기호를 만나면 _write_data로 이동한다
add r10,1 r10에 1을 더해줌으로써  다음 비교 준비
jmp _find_memory _find_memory로 다시 복귀함으로써 비교 시작
_write_data: 이어붙이는 곳,,
xor rax,rax rax 0으로 초기화
mov al,[rsi+r9] al에 rsi(이어붙일 문자열)+r9의 주소값에 저장된 값을 대입
mov [destination_string+r10],al 이어붙임을 당할 문자열 끝에 위에서 al에 대입받은 문자 이어붙임
cmp al,0 입력받은 이어붙일 문자열이 널문자를 만난다면 strcat 끝
je _strcat_end 프로그램 종료시키러 이동
inc r9 다음 문자를 받기 위해 1을 더함
inc r10 이어붙임을 당할 문자열도 다음 주소에 대입해야되기 때문에 1을 더함
jmp _write_data 다음 문자 이어붙이러 위로 이동
_strcat_end: 종료 시키기
leave 종료
ret

 

 

 

 

 

 

 

 

과제로 나온 함수 하나정도는 직접 구현해보고 싶다는 욕심에 많이 늦어져서 죄송합니다.

반응형

댓글

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • 지옥방 3주차 과제 달고나 문서 뿌시기!

    지옥방 3주차 과제 달고나 문서 뿌시기!

    2020.04.23
  • 지옥방 3주차 과제 DreamHack-System Exploitation Fundamental

    지옥방 3주차 과제 DreamHack-System Exploitation Fundamental

    2020.04.21
  • 지옥방 웹해킹 1주차 과제

    지옥방 웹해킹 1주차 과제

    2020.03.30
  • 웹해킹 1주차 PHP

    웹해킹 1주차 PHP

    2020.03.30
다른 글 더 둘러보기

정보

caputdraconis 블로그의 첫 페이지로 이동

caputdraconis

  • caputdraconis의 첫 페이지로 이동

검색

메뉴

    카테고리

    • 분류 전체보기 (168)
      • Cloud (3)
      • Computer Network (12)
      • Database (2)
      • Terraform (2)
      • 🥚고리즘 (13)
      • 겅부겅부🙃 (10)
        • Naver CS50 코칭스터디 (2)
        • Machine Learning (1)
        • Computing System (6)
      • 언어&프레임워크 (20)
        • Python (4)
        • Django (10)
        • Node JS (1)
        • C++ (2)
        • Java (1)
        • Flutter (2)
      • Security (76)
        • WebHacking Study (11)
        • 지옥방 스터디 (22)
        • 여름방학 스터디 (2)
        • PWN Study (6)
        • SUA Reversing Study (3)
        • PWN (3)
        • WebHacking (20)
        • Reversing (4)
      • 알고 있으면 도움되지 않을까,,? (23)
      • 일상다반사 (1)
      • 근황 정리 (1)
      • 42 Seoul (1)
        • Setting (1)

    최근 글

    인기 글

    댓글

    공지사항

    아카이브

    태그

    • 드림핵
    • Python
    • 웹해킹.kr
    • 파이썬
    • old-16
    • 파이썬기초
    • 리스트함수
    • 파이썬함수

    나의 외부 링크

    • Github
    • solved.ac
    • caputdraconis@kakao.com

    정보

    caputdraconis의 caputdraconis

    caputdraconis

    caputdraconis

    블로그 구독하기

    • 구독하기
    • RSS 피드

    방문자

    • 전체 방문자
    • 오늘
    • 어제

    티스토리

    • 티스토리 홈
    • 이 블로그 관리하기
    • 글쓰기
    Powered by Tistory / Kakao. Copyright © caputdraconis.

    티스토리툴바