본문 바로가기

스터디/파이썬 해킹 프로그래밍

환경 구축 및 동적 라이브러리

환경구축

노트북에 설치된 우분투 14.10 기준.

sudo apt-get install python-2.7.8 -y


책 내용:

# cd /usr/local

# wget http://python.org/ftp/python/2.5.1/Python-2.5.1.tgz 

# tar -xvfz Python-2.5.1.tgz

# mv Python-2.5.1.tgz Python25

# cd Python25


"그러면 파이썬 소스코드가 다운로드 돼 /usr/local/Python25에 압축 해제 된다. 다음에는 파이썬 소스코드를 컴파일하고 파이썬 인터프리터가 제대로 동작하는지 확인한다.


# ./configure --prefix=/usr/local/Python25

# make && make install

# pwd

/usr/local/Python25

# python


ctypes

ctypes 는 파이썬 개발자가 이용할 수 있는 가장 강력한 라이브러리 중 하나.

ctypes 라이브러리는 동적 링크 라이브러리 함수의 호출을 가능하게 함.

복잡한 C 데이터 타입을 사용 가능하게 함.

메모리를 관리하는 로우레벨(low level) 함수들을 제공

--> ctypes 에 대한 사용방법, 관련 지식이 필요함.


동적 라이브러리 활용


ctypes 라이브러리를 이용하는 첫 번째 단계는 그것이 동적 링크 라이브러리의 함수를 어떻게 해석해 접근하는지 그 방법을 이해하는 것.

동적으로 링크되는 라이브러리는 컴파일된 바이너리로서 프로세스가 실행될 때 해당 프로세스에 동적으로 링크됨.

윈도우 플랫폼에서는 이를 동적 링크 라이브러리(DLL, Dynamic Linked Library)라고 부름.

리눅스 에서는 공유 객체 (SO, Shared Object) 라고 함.

이 두 개(DLL,SO)는 외부에 EXPORT 주소를 제공함. 그리고 이 주소를 이용해 해당 함수의 실제 메모리상 주소를 구함.


--> 

동적 링크 라이브러리는 컴파일된 바이너리.

프로세스 실행시 해당 프로세스에 동적으로 링크됨. 

하지만 이 라이브러리는 본래 실행시에 동적 라이브러리가 제공하는 함수의 메모리 상 주소를 구해야 하지만,

ctypes 라이브러리를 이용하면 이런 작업들을 생략할 수 있다.

이름 -> 메모리 주소



이 ctypes 라이브러리 에서는 세 가지 방법으로 동적 라이브러리를 로드할 수 있는데, 

cdll(), windll(), oledll()

이 세가지 방법의 차이점은 export 함수를 호출하는 방법과 리턴 값을 반환하는 방법에 있음. 


1. cdll() 

- 표준 cdecl 호출 규약을 이용하는 함수를 export 하는 라이브러리를 로드하는데 사용.


2. windll()

- 마이크로소프트 Win32 API가 사용하는 stdcall 호출 규약을 이용하는 함수를 export 하는 라이브러리를 로드하는데 사용


3. oledll()

- windll() 방법과 동일하게 동작하지만, export 함수가 반환하는 값이 HRESULT 라 가정한다.

HRESULT -> 마이크로소프트 컴포넌트 객체 모델 (COM,Component Object Model)에서 여러 메시지를 반환하기 위해서 특별히 사용되는 것


#호출 규약에 따른 설명

출처: http://egloos.zum.com/qufl1999/v/2338204



__cdecl (C delcaration) 호출 규약이란?

- 함수를 호출 받은 쪽(함수의 내부) 에서 파라미터를 제거하도록 되어 있음.

따라서 파라미터의 갯수가 명확한 곳에서 사용하게 되어있음. 

--> 윈도우 API 같은 경우 함수에 따른 인자들이 명확히 정의되어 있으므로 cdecl 규약을 (주로)사용함. // 이 부분 질문


Q1. 책에서는 Win32 API가 stdcall을 사용한다고 말했는데, 어떤 곳에서는 윗 부분처럼 함수의 파라미터가 명확하게 정의되어 있어 함수 내부에서 파라미터를 pop하는 cdecl 방식을 주로 이용한다고 함. 진실은?


__stdcall (standard call) 

- 함수를 호출한 쪽에서 return 받은 후 stack에서 제거함. 따라서 호출한 함수가 자신의 파라미터를 알고 있기 때문에 printf 와 같이 파라미터가 가변적인 함수에서 사용가능 함.

--> 아마 스택에서 제거하는 방식이 가변형 파라미터에 대해 유연하게 대처할 수 있으므로 그런듯. // 질문

 

Q2. 스택에서 꺼낼 때, 함수내에서 pop 하는 것과 호출자에서 pop 하는 것중 무엇이 다르며, 왜 함수 내에서 가변적인 파라미터를 스택에서 pop 할 수 없는가.

(아래에 답)


CDeclFunction(g_szCdeclCall, 1, 'a'); 

// 아래는 실제 어셈블리 코드 

push 61h   // 'a' 를 스택에 넣습니다. 

push 1     // 1 를 스택에 넣습니다. 

mov  eax, [g_szCdeclCall (00403014)]   

push eax   // 문자열을 넣습니다. 

call CDeclFunction (0040105c) 

add esp,0Ch  // parameter 를 제거합니다. 


특징:

파라미터를 오른쪽 부터 왼쪽 순으로 넣음.

add ~ --> 파라미터를 스택에서 제거



StdCallFunction ( g_szStdCall, 2, 'b');


// 아래는 실제 어셈블리 코드

push 62h  // 'b'를 스택에 넣습니다.

push 2    //  2를 스택에 넣습니다.

mov  ecx, dword ptr [g_szStdCall (00403010)]

push ecx // 문자열을 넣습니다.

call StdCallFunction (00401069) // 함수를 호출합니다. 


특징:

파라미터를 오른쪽부터 왼쪽 순으로 넣음.

함수내에서 파라미터를 제거하는 과정이 없음.

대신, 함수 내에서 파라미터를 제거.

(함수 내에, 

ret 0Ch 라는 어셈블리 구문이 있는데, 이 구문은 그냥 리턴하지 말고 0Ch 만큼 이동함.


------------- 정리 ----------------

stdcall cdecl 공통점

- 함수의 인자를 스택에 넣을 때 오른쪽에서 왼쪽으로 쌓음. (가장 첫 번째 인자가 스택의 맨 위)


stdcall cdecl 차이점

stdcall:

- 호출된 함수, stdcall로 정의된 함수 내부에서 스택이 정리됨. (pop됨)


cdecl:

- 호출한 함수, cdecl로 정의된 함수 외부에서 스택이 정리됨.

--> 이러한 특징 덕에 cdecl로 정의된 함수는 가변 인자를 지원함.


A2. 

호출한 함수에서는 가변 인자의 스택의 크기를 알지만 호출된 함수에서는 가변 인자의 스택의 크기를 알 수 없기 때문. 


Total

- stdcall 방식은 cdecl 방식보다 빠르지만 가변 인자를 지원하지 못함.

- OS가 호출하는 함수, DLL에 들어가는 함수는 가능하면 stdcall을 사용. (stdcall이 표준)



예제:

# ctypes를 이용, 변수 seitz에 "loves the python" 문자열의 포인터를 할당하는 방법

from ctypes import *

>>> c_int()

c_long(0)

>>> c_char_p("Hello world!")

c_char_p('Hello world!')

>>> c_ushort(65531)

>>> 

>>> seitz = c_char_p("loves the python")

>>> print seitz

c_char_p('loves the python')

>>> print seitz.value

loves the python

>>> exit()


C 언어 에서는 포인터의 값에 대해 *로 접근했지만, python 에서는 .value 로 접근하는 듯 하다.


구조체 정의 

C 언어

struct beer_recipe

{

 int amt_barley;

 int amt_water;

};



Python

class beer_receipe(Structure):

 _fields_ = [

  ("amt_barley",c_int),

  ("amt_water",c_int),

 ]


ctypes에서의 구조체 정의는 C언어와 매우 흡사.


union 은 struct 와 매우 비슷하지만, union 에서는 "모든 멤버 변수가 동일한 메모리 공간을 공유" 한다는 점이 다르다.

동일한 메모리 공간을 공유하기에 union 에서는 서로 다른 데이터 타입에 동일한 값을 저장할 수 있다.


union 정의

C 언어

union {

 long barley_long;

 int barley_int;

 char barley_char[8];

}barley_amount;


Python

class barley_amount(Union):

 _fields_ = [

 ("barley_long",c_long),

 ("barley_int",c_int),

 ("barley_char",c_char * 8),

 ]


barley_amount union의 barley_int 멤버 변수에 66 할당 시, 다른 멤버 변수 barley_char를 이용해 그 값에 해당하는 문자를 출력할 수 있다.

(

barley_int = 66 일 때,

printf("%c",barley_char);

-----result-----

B



예제


chapter1-unions.py

from ctypes import *

class barley_amount(Union):

 _fields_ = [

 ("barley_long",c_long),

 ("barley_int",c_int),

 ("barley_char",c_char * 8),

 ]


value = raw_input("Enter the amount of barley to put into the beer vat: ")

my_barley = barley_amount(int(value))

print "Barley amount as a long: %ld" % my_barley.barley_long

print "Barley amount as a int: %d" % my_barley.barley_int

print "Barley amount as a char: %s" %my_barley.barley_char


---------- result -----------

Enter the amount of barley to put into beer vat: 66

Barley amount as a long: 66

Barley amount as a int: 66

Barley amount as a char: B # Ascii 기준 66 은 'B'



--> ctypes 에서는 배열을 배열 요소 갯수를 (인덱스 갯수를) 해당 요소 타입에 곱하는 형태로 정의.

(c_int * 8) == int asdf[8];










'스터디 > 파이썬 해킹 프로그래밍' 카테고리의 다른 글

스터디 질문 답변  (0) 2015.01.14
2015-01-15 스터디  (0) 2015.01.12
2015-01-12 질문 정리  (0) 2015.01.12
레지스터 / 브레이크 포인트  (0) 2015.01.11
스터디 OT (feat. Skype)  (0) 2015.01.11