개요
Python으로 Windows OS에서 제공되는 강력한 기능을 활용하려면 Win32 API를 활용해야 한다.
이를 위해 Python용 외부 함수(foreign function) 라이브러리인 ctypes를 활용해 보자.
ctypes는 C 호환 데이터형을 제공하며, DLL 또는 공유 라이브러리에 있는 함수를 호출할 수 있다.
또한 Windows, Linux, Unix, OS X, Android 등 다양한 운영체제에서 지원하는 Native Library를 사용할 수 있는 강력한 도구이기도 하다.
ctypes는 동적 라이브러리 호출 절차를 단순화하고, 복잡한 C 데이터 형을 지원하며 Low Level 함수를 제공한다는 장점이 있다.
DLL 로딩
ctypes는 cdll, windll, oldell 호출 규약을 지원한다.
ctypes |
지원하는 호출 규약 |
비고 |
cdll |
cdecl |
|
windll |
stdcall |
|
oledll |
stdcall |
반환 값을 HRESULT로 가정함 |
DLL 로드를 위해 cdll을, Windows에서는 windll, oledll 객체를 사용한다. cdll은 표준 cdecl, windll은 stdcall 호출 규약을 사용하여 함수를 호출한다.
oledll 또한 windll처럼 stdcall 호출 규약을 사용하는데, 함수가 HRESULT 에러 코드를 return한다고 가정한다는 차이점이 있다.
에러 코드는 함수 호출이 실패할 때 OSError 예외를 자동으로 발생시키는 데 사용된다.
1 2 3 4 5 | >>> from ctypes import * >>> print windll.kernel32 <WinDLL 'kernel32', handle 771e0000 at 2f2e950> >>> print cdll.msvcrt <CDLL 'msvcrt', handle 76260000 at 2f22670> | cs |
Windows 용 예제. msvcrt는 대부분의 표준 C 함수가 포함된 Microsoft 표준 C 라이브러리이며, cdecl 호출 규약을 사용한다. 1
윈도우는 일반적으로 .dll 파일 접미사를 자동으로 추가한다.
Linux에서는 라이브러리를 로드하려면 확장자를 포함하는 파일명을 지정해야 하므로, 어트리뷰트 액세스로 라이브러리를 로드할 수 없고, DLL Loader의 LoadLibrary() 함수를 사용하거나 cdll의 생성자를 호출하여 로드해야 한다.
Win32 API 호출
1 2 3 4 5 6 | >>> from ctypes import * >>> libc = cdll.msvcrt >>> printf = libc.printf >>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double] >>> printf("String '%s', Int %d, Double %f\n", "Hi", 10, 2.2) String 'Hi', Int 10, Double 2.200000 | cs |
1 | libc.strchr.restype = c_char_p | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | >>> from ctypes import * >>> libc = cdll.msvcrt >>> libc.printf <_FuncPtr object at 0x0361A828> >>> print windll.kernel32.GetModuleHandleA <_FuncPtr object at 0x0361A990> >>> print windll.kernel32.MyOwnFunction Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\python27\lib\ctypes\__init__.py", line 375, in __getattr__ func = self.__getitem__(name) File "C:\python27\lib\ctypes\__init__.py", line 380, in __getitem__ func = self._FuncPtr((name_or_ordinal, self)) AttributeError: function 'MyOwnFunction' not found >>> | cs |
kernel32와 user32와 같은 Win32 시스템 DLL은 ANSI 뿐만 아니라 유니코드 버전 함수도 사용할 수 있다. 유니코드 버전은 이름에 W가 추가로 붙어있고, ANSI 버전은 이름에 A가 붙어 있다.
1 2 | HMODULE GetModuleHandleA(LPCSTR lpModuleName); # ANSI HMODULE GetModuleHandleW(LPCWSTR lpModuleName); # UNICODE | cs |
GetModuleHandle 함수는 인자로 받은 모듈 이름의 모듈 핸들을 반환한다. 위의 코드는 GetModuleHandle() 함수의 C 프로토타입이며, ANSI인지, 유니코드인지에 따라 GetModuleHandleA() 나 GetModuleHandleW를 명시적으로 지정하여 각각 필요한 버전에 따라 사용한다. 2
유효한 Python 식별자가 아닌 이름으로 함수를 내보낼 때에는 getattr() 함수를 사용하여 함수를 조회해야 한다. 3
1 2 | >>> getattr(cdll.msvcrt, "??2@YAPAXI@Z") <_FuncPtr object at 0x0361AA08> | cs |
윈도우에서 일부 dll은 이름 대신 서수(ordinal)로 함수를 내보낸다. 아래 코드는 dll 객체를 인덱싱하여 액세스할 수 있도록 하는 간단한 함수를 구현한 것이다.
1 2 3 4 5 6 7 8 9 | >>> cdll.kernel32[1] <_FuncPtr object at 0x0361AA80> >>> cdll.kernel32[0] Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\python27\lib\ctypes\__init__.py", line 380, in __getitem__ func = self._FuncPtr((name_or_ordinal, self)) AttributeError: function ordinal 0 not found >>> | cs |
자료형
1 2 3 4 5 | >>> from ctypes import * >>> libc.strchr.restype = c_char_p >>> i = c_int(42) >>> print i.value 42 | cs |
1 | PI = POINTER(c_int) | cs |
ctypes형 |
C형 |
Python 형 |
c_bool |
_Bool |
bool(1) |
c_char |
char |
1 문자 바이트열 객체 |
c_wchar |
wchar_t |
1 문자 문자열 |
c_byte |
char |
int |
c_ubyte |
unsigned char |
|
c_short |
short |
|
c_ushort |
unsigned short |
|
c_int |
int |
|
c_uint |
unsigned int |
|
c_long | long | |
c_ulong | unsigned long | |
c_longlong | __int64 또는 long long | |
c_ulonglong | unsinged __int64 또는 unsigned long long | |
c_size_t | size_t | |
c_ssize_t | ssize_t 또는 Py_ssize_t | |
c_float | float | float |
c_double | double | |
c_longdouble | long double | |
c_char_p | char * (NULL 종료됨) | 바이트열 객체 또는 None |
c_wchar_p | wchar_t * (NULL 종료함) | 문자열이나 None |
c_void_p | void * | int 또는 None |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >>> s = "Hello, World!" >>> c_s = c_wchar_p(s) >>> print c_s c_wchar_p(139966785747106) >>> print c_s.value Hello World >>> c_s.value = "Hi, there!" >>> print c_s c_wchar_p(139966783348656) >>> print c_s.value Hi, there! >>> print s Hello, World! >>> | cs |
포인터의 전달
1 2 3 4 5 6 7 8 9 10 11 12 13 | >>> from ctypes import * >>> libc = cdll.msvcrt >>> >>> i = c_int() >>> f = c_float() >>> s = create_string_buffer(b'\000' * 32) >>> print i.value, f.value, repr(s.value) 0 0.0 '' >>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s", byref(i), byref(f), s) 3 >>> print i.value, f.value, repr(s.value) 1 3.1400001049 'Hello' >>> | cs |
콜백 함수 7
1 2 3 4 | >>> IntArray5 = c_int * 5 >>> ia = IntArray5(5, 1, 7, 33, 99) >>> qsort = libc.qsort >>> qsort.restype = None | cs |
1 | >>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | from ctypes import * libc = cdll.msvcrt IntArray5 = c_int * 5 ia = IntArray5(5, 1, 7, 33, 99) qsort = libc.qsort qsort.restype = None CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) def py_cmp_func(a, b): print "py_cmp_func", a[0], b[0] return 0 cmp_func = CMPFUNC(py_cmp_func2) qsort(ia, len(ia), sizeof(c_int), cmp_func) | cs |
1 2 3 | def py_cmp_func(a, b): print "py_cmp_func", a[0], b[0] return a[0] - b[0] | cs |
구조체
1 2 3 4 5 6 7 8 9 10 11 12 | from ctypes import * class POINT(Structure): _fields_ = [("x", c_int), ("y", c_int)] point = POINT(10, 20) print(point.x, point.y) point = POINT(y=5) print(point.x, point.y) POINT(1, 2, 3) print(point.x, point.y) | cs |
- ※ 주의 : cdll.msvct를 통해 표준 C 라이브러리에 액세스하면 Python에서 사용되는 라이브러리와 호환되지 않는 오래된 라이브러리 버전이 사용된다. 가능하면 Python 자체의 기능을 사용하거나 msvcrt 모듈을 import 해서 사용하는 것을 권장한다. [본문으로]
- 핸들(handle) : API에서 핸들은 32bit 크기의 숫자로 객체를 참조하는 것이다. Windows의 핸들은 C언어나 MS-DOS 프로그래밍의 파일 핸들과 유사하다. 프로그램은 거의 항상 Windows 함수를 호출함으로써 핸들을 얻는다. [본문으로]
- getattr(object, name[, default]) : 주어진 이름의 object 어트리뷰트를 반환한다. [본문으로]
- create_string_buffer(init_or_size, size = NONE) : 가변 문자 바퍼 생성. 반환된 객체는 ctypes c_char 배열이다. [본문으로]
- sscanf(const char *buffer, const char *format, argument-list); : buffer에서 argument-list가 제공하는 위치로 데이터를 읽는다. 각 argument는 format-string에서 유형 지정자에 대응하는 유형의 변수에 대한 포인터이다. [본문으로]
- repr(object) : 객체의 인쇠 가능한 표현을 포함한 문자열 반환 [본문으로]
- 콜백 함수(Callback function) : 다른 함수의 인자로써 이용되는 함수, 또는 어떤 이벤트에 의해 호출되어지는 함수 [본문으로]
- ※ 주의 : C 코드에서 사용되는 동안, CFUNCTYPE() 객체에 대한 참조를 유지해야 한다. ctypes가 계속 참조하고 있지는 않으며, 사용자가 직접 하지 않는다면 콜백이 발생할 때 프로그램이 충돌할 수도 있다. [본문으로]
'System > Cybersecurity' 카테고리의 다른 글
pydbg 모듈을 활용한 API 후킹 - (1) 디버거의 개념과 pydbg 모듈 설치 (0) | 2019.03.25 |
---|---|
애플리케이션 해킹 개요 - Windows 애플리케이션의 기본 개념 (0) | 2019.03.23 |
레지스터의 종류 - 범용 레지스터, 세그먼트 레지스터, 플래그 레지스터 (0) | 2019.03.09 |
정보보안 단어 요약정리 (0) | 2018.12.01 |
Ubuntu 16.04 Privilege Escalation 취약점 공격 실습 - root 권한 얻기 (0) | 2018.12.01 |