시스템 해킹을 위한 기초 지식 모두 닫기

어셈블리어 프로그램 작성하고 실행하기

실습 환경 구성
실습 환경: Windows XP
필요 프로그램: MASM32 SDK, WINASM

다음의 코드를 컴파일한 다음 실행한다.

ASM_TEST.asm

    .486					; 32비트 모드임을 알려준다.
    .model flat, stdcall	; 메모리 모델을 알려준다.
 
; 라이브러리의 표준 양식이 있는 inc 파일과 라이브러리 파일을 include
    include \masm32\include\windows.inc     
    include \masm32\macros\macros.asm

    include \masm32\include\masm32.inc
    include \masm32\include\gdi32.inc
    include \masm32\include\user32.inc
    include \masm32\include\kernel32.inc

    includelib \masm32\lib\masm32.lib
    includelib \masm32\lib\gdi32.lib
    includelib \masm32\lib\user32.lib
    includelib \masm32\lib\kernel32.lib

    .code				; MASM이 시작됨을 알려준다.

start:					; 코드의 시작점(Entry Point)
    call main			; main 함수를 호출한다.
    exit

main proc				; main 함수 
    mov eax, 100		; 100을 EAX 레지스터에 저장한다.
    mov ecx, 250		; 250을 ECX 레지스터에 저장한다.
    add ecx, eax		; ECX에 EAX 값을 더한다.

    mov eax, 300		; 300을 EAX 레지스터에 저장한다.
    cmp eax, ecx		; EAX와 ECX를 비교한다.

    je equal			; EAX와 ECX가 같으면 "equal"로 점프
    jg bigger           ; EAX가 ECX보다 크면 "bigger"로 점프
    jl smaller          ; EAX가 ECX보다 작으면 "smaller"로 점프

  equal:
    print chr$("EAX and ECX are same.")
    jmp over			; "over"로 점프한다.

  bigger:
    print chr$("EAX is bigger than ECX.")
    jmp over

  smaller:
    print chr$("EAX is smaller than ECX.")

  over:
    ret                 ; return

main endp               ; main 함수를 끝낸다.

end start               ; MASM 프로그램을 끝낸다.

EAX에 저장된 300은 ECX에 저장된 350보다 작다. 코드가 올바르게 실행되었다.

프로그램 실행 과정에 따른 스택의 동작 이해하기

실습 환경 구성
실습 환경: RedHat 6.2
필요 프로그램: gcc, gdb

sample.c

void main() {
	int c;
	c = function(1, 2);
}

int function(int a, int b) {
	char buffer[10];
	a += b;
	return a;
}

위의 코드를 작성하여 컴파일한 다음, vi 편집기로 열어본다.

# gcc -S -o sample.asm sample.c
# vi sample.asm
sample.asm

        .file   "sample.c"
        .version        "01.01"
gcc2_compiled.:
.text
        .align 4
.globl main
        .type    main,@function
main:
        pushl %ebp
        movl %esp,%ebp
        subl $4,%esp
        pushl $2
        pushl $1
        call function
        addl $8,%esp
        movl %eax,%eax
        movl %eax,-4(%ebp)
.L1:
        leave
        ret
.Lfe1:
        .size    main,.Lfe1-main
        .align 4
.globl function
        .type    function,@function
function:
        pushl %ebp
        movl %esp,%ebp
        subl $12,%esp
        movl 12(%ebp),%eax
        addl %eax,8(%ebp)
        movl 8(%ebp),%edx
        movl %edx,%eax
        jmp .L2
        .p2align 4,,7
.L2:
        leave
        ret
.Lfe2:
        .size    function,.Lfe2-function
        .ident  "GCC: (GNU) egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)"
  1. (line 9) pushl %ebp
    • 최초의 프레임 포인터(ebp) 값을 스택에 저장한다.
    • ebp는 함수 시작 전의 기준점이 되며, 스택에 저장된 ebp를 SFP(Saved Frame Pointer)라고 한다.
  2. (line 10) movl %esp, %ebp
    • 현재의 esp 값을 ebp 레지스터에 저장한다.
  3. push %ebp
    movl %esp, %ebp
    위의 명령어 두 줄은 새로운 함수를 시작할 때 항상 똑같이 수행하는 명령으로, 프롤로그(Prologue)라 부르기도 한다.
  4. (line 11) subl $4,%esp
    • esp 값(int c 할당값)에서 4바이트만큼 뺀다.
    • 즉 스택에 4바이트만큼의 용량을 할당한다.
  5. (line 12) pushl $2

    (line 13) pushl $2

    (line 14) call function

    • 스택에 2개의 변수를 저장한 다음 function 함수를 호출한다.
  6. (line 27) pushl %ebp

    (line 28) movl %esp,%ebp

    • function(1, 2)의 시작에서도 프롤로그 명령이 실행되었다.
  7. (line 29) subl $12,%esp
    • char buffer[10]의 공간을 스택에 할당하기 위해 esp 값에서 12바이트만큼 뺀다.
    • 10바이트만큼 할당되도록 코딩했으나, 4바이트 단위로 할당되므로 12바이트가 할당된다.
  8. (line 30) movl 12(%ebp),%eax
    • ebp에 12바이트를 더한 주소 값의 내용(정수 2)을 eax 값에 복사한다.
  9. (line 31) addl %eax,8(%ebp)
    • ebp에 8바이트를 더한 주소 값의 내용(정수 1)에 eax 값을 더한다.
    • 바로 전에 eax에 2를 저장했으므로 8(%ebp) 값은 3이 된다.
  10. (line 32) movl 8(%ebp),%edx
    • ebp에 8바이트 더한 주소 값의 내용(정수 3)을 edx에 저장한다.
    • 즉 a += b 결과값을 저장하는 과정이다.
  11. (line 33) movl %edx,%eax
    • edx에 저장된 정수 3을 eax로 복사한다.
  12. (line 34) jmp .L2
    • L2로 점프한다.
  13. (line 37) leave
    • 함수를 끝낸다.
  14. (line 38) ret
    • function 함수를 마치고 function 함수에서 저장된 ebp 값을 제거한다.
    • 그리고 main 함수의 원래 ebp 값으로 EBP 레지스터 값을 변경한다.

셸 실행 과정을 이해하고, 셸 코드 작성하기

실습 환경 구성
실습 환경: RedHat 6.2
필요 프로그램: gcc, gdb

  1. 셸 기본 코드 획득
    • 셸 기본 코드를 만들려면 기본 실행 코드부터 준비해야 한다.
    • 셸을 실행하기 위한 C언어 코드를 컴파일하여 어셈블리 코드를 획득하고, gdb를 이용하여 분석한다.
    shell.c
    
    #include 
    #include 
    
    int main(){
    	char *name[2];
    	name[0] = "/bin/sh";
    	name[1] = 0;
    	execve(name[0], name, NULL);
    }
    # gcc -o shell shell.c
    # gdb shell
    (gdb) disass main
    어셈블리어 코드
    
    Dump of assembler code for function main:
    0x80483c8 <main>:       push   %ebp
    0x80483c9 <main+1>:     mov    %esp,%ebp
    0x80483cb <main+3>:     sub    $0x8,%esp
    0x80483ce <main+6>:     movl   $0x8048440,0xfffffff8(%ebp)
    0x80483d5 <main+13>:    movl   $0x0,0xfffffffc(%ebp)
    0x80483dc <main+20>:    push   $0x0
    0x80483de <main+22>:    lea    0xfffffff8(%ebp),%eax
    0x80483e1 <main+25>:    push   %eax
    0x80483e2 <main+26>:    mov    0xfffffff8(%ebp),%eax
    0x80483e5 <main+29>:    push   %eax
    0x80483e6 <main+30>:    call   0x80482e8 <execve>
    0x80483eb <main+35>:    add    $0xc,%esp
    0x80483ee <main+38>:    leave
    0x80483ef <main+39>:    ret
    End of assembler dump.
  2. 셸 기본 코드 분석

    0x80483c8 <main>:       push   %ebp
    0x80483c9 <main+1>:     mov    %esp,%ebp
    0x80483cb <main+3>:     sub    $0x8,%esp
    함수 프롤로그를 실행하고 스택에 8바이트의 공간(char *name[2])을 할당한다.
    
    0x80483ce <main+6>:     movl   $0x08048440,0xfffffff8(%ebp)	; name[0] = "/bin/sh"
    ebp에서 0xfffffff8(-8) 거리에 있는 주소에 0x08048440 값을 저장한다.
    0x08048440 주소에는 "/bin/sh" 문자열이 저장되어 있다.
    
    0x80483d5 <main+13>:    movl   $0x0,0xfffffffc(%ebp)		; name[1] = 0
    ebp에서 0xfffffffc(-4) 거리에 있는 주소에 0x0 값을 저장한다.
    
    0x80483dc <main+20>:    push   $0x0	; execve(name[0], name, NULL)
    0(NULL)을 스택에 저장한다.
    
    0x80483de <main+22>:    lea    0xfffffff8(%ebp),%eax
    lea(Load Effective Address)는 해당 메모리의 주소를 계산하는 명령이다.
    -8(%ebp)에 저장된 값 0x08048440(/bin/sh)이 아닌, -8(%ebp)의 주소 0xbfffe830을 %eax 값에 저장한다.
    
    0x80483e1 <main+25>:    push   %eax	; execve(name[0], name, NULL)
    eax 값(0xbfffe830)을 스택에 저장한다.
    /bin/sh 실행 시 인수를 입력받는데, 셸을 획득한 후 사용자가 입력하는 실질 명령에 해당하는 포인터 값이다.
    
    0x80483e2 <main+26>:    mov    0xfffffff8(%ebp),%eax
    eax의 ebp에서 0xfffffff8(-8) 거리에 있는 주소값(0x08048440)을 저장한다.
    
    0x80483e5 <main+29>:    push   %eax
    스택에 eax 값을 저장한다.
    이 값은 결국 -8(ebp) 값과 동일하게 "/bin/sh" 문자열이 있는 곳에 대한 주소값이다.
    
    0x80483e6 <main+30>:    call   0x80482e8 <execve>
    execve에 대한 인수를 모두 스택에 저장했으므로 execve를 호출한다.
    (gdb) disass execve
  3. 셸 기본 코드 정리
  4. 셸 기본 코드 재코딩
  5. 셸 실행

리눅스/유닉스에서 파일에 대한 접근 권한 설정하기

실습 환경: Fedora 14

  1. 파일과 디렉터리 생성 시 기본 권한 확인

      # touch a.txt
      # mkdir a
      # ls -al

      따로 권한 설정을 하지 않았는데, 파일은 rw-r--r--(644), 디렉터리는 rwxr-xr-x(755) 권한이 할당되었다.

      파일은 기본 생성 최고 권한이 666이고, 디렉터리는 777이다.

      이 최고 권한에서 umask값을 뺀 값이 생성 시 기본 권한이 된다.

      # vi /etc/profile

      기본 umask값이 022이므로 파일의 기본 권한은 666-022=644, 디렉터리의 기본 권한은 755-022=755이다.

  2. 파일 및 디렉터리 기본 생성 권한 변경

    id(identity) 명령은 현재 계정에 관한 권한 정보를 알려준다.

    # id
    # su root
    # id
  3. 파일 및 디렉터리 권한 변경
  4. 파일 소유자/그룹 변경

SetUID를 이용한 해킹 기법 익히기

SYSTEM 권한 획득하기

시스템 해킹 모두 닫기

윈도우 XP, 2008, 7 패스워드 크래킹하기

리눅스 패스워드 크래킹하기

서비스 데몬 패스워드 크래킹하기

윈도우 패스워드 복구하기

리눅스 패스워드 복구하기

비주얼 C++ 컴파일러 사용법 익히기

바이너리 파일 수정을 통해 리버스 엔지니어링 공격하기

프로그램 로직 분석을 통해 리버스 엔지니어링 공격하기

UPX 패킹하기

심볼릭 링크 기능 알아보기

레이스 컨디션 수행하기

gdb 분석을 통해 취약 프로그램의 스택 오버플로우 개념 이해하기

스택 버퍼 오버플로우 수행하기

gdb 분석을 통해 취약 프로그램의 힙 버퍼 오버플로우 개념 이해하기

힙 버퍼 오버플로우 수행하기

rtl 공격 수행하기

canary 확인하기

포맷 스트링 공격 원리 이해하기

포맷 스트링 공격 수행하기

SetUID형 로컬 백도어 설치하고 이용하기

윈도우 백도어 설치하고 이용하기

자동 실행형 백도어 설치하고 이용하기

윈도우 백도어 탐지하고 제거하기

리눅스 백도어 탐지하고 제거하기

tripwire를 이용한 무결성 검사하기

운영체제 보안 모두 닫기

윈도우 FTP 서비스 보안 설정하기

윈도우 터미널 서비스 보안 설정하기

윈도우 방화벽 규칙 적용하기

파일과 디렉터리 권한 설정하기

유닉스에서 관리자 계정 생성하기

FTP Anonymous 접속 설정하기

SSH 접속하기

XDMCP 접근 제어하기

TCP Wrapper를 이용한 접근 제어 적용하기

로그와 침입 탐지 모두 닫기

윈도우 로그 관리하기

적절한 로깅 환경 설정하기

BSM 툴 설치하고 이용하기

윈도우 로그 정책 변경하고 기존 로그 삭제하기

윈도우 메모리 덤프하기

시스템 이미지 획득하기

삭제 파일 복구하기

리눅스 시스템 로그 삭제하기

리눅스 시스템 메모리 덤프하기

리눅스 시스템 이미지 획득하기