Hello, World 출력 Shellcode
printf를 이용하여 콘솔에 Hello, World를 출력하는 프로그램은 쉽게 짤 수 있다.
#includeint 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
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)
...
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)
...
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