Hello, World 출력 Shellcode

printf를 이용하여 콘솔에 Hello, World를 출력하는 프로그램은 쉽게 짤 수 있다.


#include 
int main()
{
    printf("Hello, World");
    return 0;
}

windows 시스템에서 printf 함수는 msvcrt.dll에 포함되어 있다. Shellcode를 만들기 위해서 해당라이브러리를 로딩하는 코드를 작성해야 한다. 라이브러리 로딩함수는 LoadLibrary함수를 사용하고, 이 함수는 kernel32.dll에서는 LoadLibraryA로 정의되어 있다.

kernel32.dll은 따로 로딩할 필요가 없으므로, kernel32.dll에 있는 LoadLibraryA위치를 찾으면 된다.

함수의 위치를 찾는 코드는 다음과 같다.

#include 
#include 

/***************************************
arwin - win32 address resolution program
by steve hanna v.01
   vividmachines.com
   shanna@uiuc.edu
you are free to modify this code
but please attribute me if you
change the code. bugfixes & additions
are welcome please email me!
to compile:
you will need a win32 compiler with
the win32 SDK

this program finds the absolute address
of a function in a specified DLL.
happy shellcoding!
***************************************/


int main(int argc, char** argv)
{
        HMODULE hmod_libname;
        FARPROC fprc_func;
        
        printf("arwin - win32 address resolution program - by steve hanna - v.01n");
        if(argc < 3)
        {
                printf("%s  n",argv[0]);
                exit(-1);
        }

        hmod_libname = LoadLibrary(argv[1]);
        if(hmod_libname == NULL)
        {
                printf("Error: could not load library!n");
                exit(-1);
        }
        fprc_func = GetProcAddress(hmod_libname,argv[2]);
        
        if(fprc_func == NULL)
        {
                printf("Error: could find the function in the library!n");
                exit(-1);
        }
        printf("%s is located at 0x%08x in %sn",argv[2],(unsigned int)fprc_func,argv[1]);


}


위 코드로 얻은 kernel32.dll에 있는 LoadLibraryA의 주소는 0x771d395c 이다.
LoadLibraryA 함수는 로드할 dll을 매개변수로 받으므로, 어셈블 코드에서는 이름을 스택에 넣어주면 된다.

같은 방식으로 msvcrt.dll 에 있는 printf함수의 주소를 찾고 어셈블로 콜하면 된다.

콘솔 프로그램은 exit함수를 호출해야 정상 종료가 되므로, msvcrt.dll 에 있는 exit함수의 주소를 찾아서 콜해주면 Hello, World용 어셈블 코드를 얻을 수 있다.

[SECTION .text]

global _start

_start:

	xor eax, eax
	xor ebx, ebx
	xor ecx, ecx
	xor edx, edx
	jmp short GetLibrary
LibraryReturn:
	pop ecx
	mov [ecx + 10], dl
	mov ebx, 0x771d395c
	push ecx
	call ebx

	jmp short PrintfFunc
PrintfFuncReturn:
	pop ecx
	xor edx, edx
	mov [ecx+12], dl
	push ecx
	mov ebx, 0x76c3c5b9
	call ebx
	xor eax,eax
	push eax			
	mov eax, 0x76c336aa 		;exitprocess(exitcode);
	call eax	

GetLibrary:
	call LibraryReturn
	db 'msvcrt.dllN'
		
		
		
PrintfFunc:
	call PrintfFuncReturn
	db 'Hello, WorldN'


이것을 nasm으로 컴파일하고 objdump를 이용하여 기계어 코드를 뽑아낸다. (cygwin에서 작업 혹은 Linux 시스템에서)


nasm -f elf hello.asm; ld -o hello hello.obj; objdump -d hello

objdump 결과


hello:     file format pei-i386

Disassembly of section .text:
00401000 <_start>:
  401000: 31 c0                 xor    %eax,%eax
  401002: 31 db                 xor    %ebx,%ebx
  401004: 31 c9                 xor    %ecx,%ecx
  401006: 31 d2                 xor    %edx,%edx
  401008: eb 26                 jmp    401030 <_start+0x30>
  40100a: 59                    pop    %ecx
  40100b: 88 51 0a              mov    %dl,0xa(%ecx)
  40100e: bb 5c 39 1d 77        mov    $0x771d395c,%ebx
  401013: 51                    push   %ecx
  401014: ff d3                 call   *%ebx
  401016: eb 28                 jmp    401040 <_start+0x40>
  401018: 59                    pop    %ecx
  401019: 31 d2                 xor    %edx,%edx
  40101b: 88 51 0c              mov    %dl,0xc(%ecx)
  40101e: 51                    push   %ecx
  40101f: bb b9 c5 c3 76        mov    $0x76c3c5b9,%ebx
  401024: ff d3                 call   *%ebx
  401026: 31 c0                 xor    %eax,%eax
  401028: 50                    push   %eax
  401029: b8 aa 36 c3 76        mov    $0x76c336aa,%eax
  40102e: ff d0                 call   *%eax
  401030: e8 d5 ff ff ff        call   40100a <_start+0xa>
  401035: 6d                    insl   (%dx),%es:(%edi)
  401036: 73 76                 jae    4010ae <__DTOR_LIST__+0x54>
  401038: 63 72 74              arpl   %si,0x74(%edx)
  40103b: 2e                    cs
  40103c: 64                    fs
  40103d: 6c                    insb   (%dx),%es:(%edi)
  40103e: 6c                    insb   (%dx),%es:(%edi)
  40103f: 4e                    dec    %esi
  401040: e8 d3 ff ff ff        call   401018 <_start+0x18>
  401045: 48                    dec    %eax
  401046: 65                    gs
  401047: 6c                    insb   (%dx),%es:(%edi)
  401048: 6c                    insb   (%dx),%es:(%edi)
  401049: 6f                    outsl  %ds:(%esi),(%dx)
  40104a: 2c 20                 sub    $0x20,%al
  40104c: 57                    push   %edi
  40104d: 6f                    outsl  %ds:(%esi),(%dx)
  40104e: 72 6c                 jb     4010bc <__DTOR_LIST__+0x62>
  401050: 64                    fs
  401051: 4e                    dec    %esi
00401052 <__CTOR_LIST__>:
  401052: ff                    (bad) 
  401053: ff                    (bad) 
  401054: ff                    (bad) 
  401055: ff 00                 incl   (%eax)
  401057: 00 00                 add    %al,(%eax)
 ...
0040105a <__DTOR_LIST__>:
  40105a: ff                    (bad) 
  40105b: ff                    (bad) 
  40105c: ff                    (bad) 
  40105d: ff 00                 incl   (%eax)
  40105f: 00 00                 add    %al,(%eax)
 ...

_start 부분만 16진수로 얻은 코드를 테스트 해보면 정상적으로 실행이 된다.

 char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xeb\x26\x59\x88\x51\x0a\xbb\x5c\x39\x1d\x77\x51\xff\xd3\xeb\x28\x59\x31\xd2\x88\x51\x0c\x51\xbb\xb9\xc5\xc3\x76\xff\xd3"
 			  "\x31\xc0\x50\xb8\xaa\x36\xc3\x76\xff\xd0\xe8\xd5\xff\xff\xff\x6d\x73\x76\x63\x72\x74\x2e\x64\x6c\x6c\x4e\xe8\xd3\xff\xff\xff"
 			  "\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x4e";

int main(int argc, char **argv)
{
	int *code;
	code = (int *)shellcode;
	__asm {
		jmp code;
	}
	return 0;
}


[참고자료]
http://www.vividmachines.com/shellcode/shellcode.html

+ Recent posts