Security_lab

shell code & assembly analysis

부산대보금자리 2021. 7. 20. 16:03

시스템을 공격할 때 셸 코드를 스택에 업로드해서 사용하는데 이때 코드는 스택에서 직접 실행될 수 있도록 기계어로 바꾸어 사용해야 한다. 

위와 같이 셸 코드를 작성해보고 어셈블리 코드를 보려고 한다. 

2학년 시스템 소프트웨어 시간에 attack lab과 bomb lab등을 하면서 어셈블리에 적응을 했었는데 그새 시간이 지나서 기억이 잘 나지 않는다. 기본적인 스택구조를 따르며 위에서부터 아래로 저장된다. 

우선 기본적인 레지스터의 역할이 있으므로 한번 정리하고 가고자한다. 

 

먼저 데이터 레지스터이다. 

RAX(Extended Accumulator Register) : 사칙연산 명령어에서 자동으로 사용, 리턴 레지스터

                                                                    시스템콜의 실질적인 번호를 가리키는 레지스터

RBX(Extended Base Register) : 메모리 주소를 저장하는 용도로 사용

RCX(Extended Counter Register) : CPU는 루프 카운터로 ECX를 자동으로 사용

RDX(Extended Data Register) : EAX와 같이 사용됨.

 

그 다음은 포인터 레지스터이다. ( 주소 저장) 

RSI(Extended Source Index) / RDI(Extended Destination Index)

: 문자열 출발지/목적지 주소. 확장 소스 인덱스, 확장 목적지 인덱스 레지스터. 각각 메모리 출발지와 목적지를 나타냄.

고속 메모리 전송 명령어에서 사용.

RSP(Extended Stack Pointer)

현재 스택 주소. 그러니까 스택 맨 윗쪽 주소. 스택에 있는 데이터의 주소를 지정. 계산, 데이터 전송에는 거의 사용되지 않는다

RBP(Extended Base Pointer)

: 스택 복귀 주소. 고급언어에서 스택에 있는 함수 매개변수와 지역변수를 참조하기 위해서 사용. 고급 수준의 프로그래밍 이외에 일반적 계산과 데이터 전송에서 사용되지 않아야

RIP

: 현재 명령 실행 주소

기타r8 ~ r15

: 일반적으로 함수의 매개변수로 사용

 

push %rbp : 최초의 base 프레임 포인터 값을 스택에 저장한다. 이 rbp 바로 전에 ret이 저장된다. 

rbp는 함수 시작전의 기준점이 된다. 그리고 스택에 저장된 rbp를 SFP(Saved Frame pointer)라고 부른다. 

ret에는 함수 종료시 점프할 주소 값이 저장된다. 

 

mov %rsp,%rbp : 현재의 rsp 값을 rbp 레지스터에 저장한다. 위의 push %rbp와 mov %rsp,%rbp는 새로운 함수를 시작할 때 항상 똑같이 수행하는 명령으로 프롤로그라고 부르기도 한다. 이런것은 외워 두는 것이 좋다.

 

sub $0x10,%rsp : rsp 값에서 10 바이트만큼 뺀다. 즉 스택에 10바이트의 용량을 할당한다. 

이제는 rbp와 rsp의 공간이 벌어졌고 이 용량을 쓴다고 보면 된다. 

 

lea 0xec0(%rip),%rax : rip 는 다음 시작할 명령의 주소이다. 그리고 lea는 해당주소를 저장하는 것이다. 이까지 보면 무슨내용인지 잘 모르겠다. 하지만 뒤의 내용을 통해 예측가능하다.

 

mov %rax,-0x10(%rbp) : 여기서 현재 rbp에서 -10바이트 즉 스택의 끝쪽에 %rax를 넣는다. 아마 여기에는 name[0]이 들어가야 하는데 %rax에서 이 값을 가져오므로 rip에는 "/bin/sh"의 주소가 들어 있음을 예측한다. 

 

movq $0x0 -0x8(%rbp) : 이건 rbp에서 -8이고 이건 name[1] 인 0을 넣는것이다. 

그리고 후에 mov $0x0,%edx 를 하므로 이것은 null을 edx에 넣는다.

 

그다음에 rax와 rcx에 각각 -0x10(%rbp)의 값과 주소를 넣는다. 

그리고 이 값은 rsi 에 주소값 rdi에 데이터값이 들어간다. 

그다음에 main을 call 한다. 

앞에서 레지스터 설명을 했듯이 rsi에는 문자열 출발지 주소, 그러니까 해당 "/bin/sh"으로 가기위해선 해당 메모리 주소로 가야 하니까 저 주소를 넣은것이다.

rdi는 값인데 저 값은 "/bin/sh"의 주소가 적힌 값이다. 그러니까 그 값을 가지고 "/bin/sh"문자열을 얻을수 있다. 

이를 데이터 레지스터를 통해 포인터 레지스터에 넣었다고 판단이 된다. 

 

아마 구조는 

ret

ebp

name[1]

name[0]

null

rsi

rdi 

rsp 가 될것이다. 

 

이러한 어셈블리 스택 구조를 이해한다면 어떤식으로 스택에 쌓여가는지 에측해볼수가 있다. 

그리고 셀 동작을 하는 어셈블리 코드를 어셈블리어로 짤수가 있고 이를 기계어로 변경하여 배열에 넣은채로 실행할수 있으면 해당 코드를 실행할수가 있다. 

그 부분에 대해서는 다시 포스팅해보려고한다.

'Security_lab' 카테고리의 다른 글

gdb를 통한 heap buffer overflow  (0) 2021.07.27
gdb를 통한 Stack buffer overflow  (0) 2021.07.27
레이스 컨디션 실습  (0) 2021.07.25
John the ripper,johnny 와 Hash  (0) 2021.07.23
SetUID  (0) 2021.07.21