컨텐츠 바로가기

[Python] ctypes 를 이용한 shard library 직접 호출

http://mcchae.egloos.com/11024689

홍민희 님의 Wand 라는 것을 살펴보다가,
파있썬의 ctypes 바인딩을 했다는 이야기가 나옵니다.

첨에는 뭔가 했다가, 바로 앗! 하고야 말았습니다.

기존에 C로 만든 함수와 파있썬을 붙이기 위해서는

c 함수에서 

#include <Python.h>
...

static PyObject * foo(PyObject *self, PyObject *args)
{
  ...
  if (!PyArg_ParseTuple(args,"isi", &pa, &pb, &pc)) {
    error();
  }
  ...
}

등과 같이 작업을 하였습니다.

그동안 몇번 생각을 했던 적이 있습니다.

C# 코딩을 하다보면, unmanaged dll을 호출하기 위한
dllimport 라는 함수가 있습니다.
그러면 C#에서 바로 해당 unmanaged dll 함수를 사용하는 것이지요.
파이썬에 이런게 있으면 얼마나 좋을까... 하구요.

바로 ctypes 라는 것이 이런 개념이었습니다.... ㅎㅎㅎ

바로 샘플을 만들어서 돌려보았습니다.

우선 mylib.c 라는 파일을 만들어 아래 내용을 넣어줍니다.

int sum(int i, int j)
{
    return i + j;
}

int mystrlen(char *s)
{
    char *p = s;
    for (;*p;++p);
    return p-s;
}

int hexdump(void *v, int len)
{
    unsigned char *p = v;
    int i = 0;
    for (;i<len;++i) {
        printf("%02x", p[i]);
        if (i > 0 && i % 8 == 0) printf("\n");
    }
    printf("\n");
    return len*2;
}

그 다음 다음과 같이 libmylib.so 라는 이름의 shared library 를 만듦니다.

$ gcc -c -fPIC mylib.c
$ gcc -shared mylib.o -o libmylib.so

다음에 test.py 라는 파일에 아래 내용을 넣습니다.

from ctypes import *
import os

s='Hello world?'

clib = cdll.LoadLibrary('libc.so.6')
print 'clib.time() = %d' % clib.time(None)
clib.printf("clib.printf(s) = <%s>\n", s)

mylib = cdll.LoadLibrary('%s/libmylib.so' % os.getcwd())

print 'mylib.sum(1,2) => %d' % mylib.sum(1,2)

print 'mystrlen("%s") => %d' % (s,mylib.mystrlen(s))

v=[]
v.append('a')
v.append('0')
v.append('1')
v.append(chr(0))
v.append('2')
v.append('3')
s = ''.join(v)
print 'python s = <%s>' % s
r = mylib.hexdump(s,len(s))
print "mylib.hexdump(s,len(s)) = %s" % r

위와 같이 내용을 넣고 실행하면,

$ python test.py 
clib.time() = 1369726019
clib.printf(s) = <Hello world?>
mylib.sum(1,2) => 3
mystrlen("Hello world?") => 12
python s = <a0123>
613031003233
mylib.hexdump(s,len(s)) = 12

와 같이 원하는 결과가 나옵니다.

call by reference 처럼 결과를 받아오는 것도 필요합니다.

>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer('\000' * 32)
>>> print i.value, f.value, repr(s.value)
0 0.0 ''
>>> libc.sscanf("1 3.14 Hello", "%d %f %s",
...             byref(i), byref(f), s)
3
>>> print i.value, f.value, repr(s.value)
1 3.1400001049 'Hello'

와 같이 패러미터를 위한 방을 미리 c_int(), c_float(), create_string_buffer() 등으로 만든 다음
결과를 받아오면 됩니다.


참고로 C의 구조체를 다룰 수도 있는데,
다음과 같이 참고하면 됩니다.

class WrappedKey(Structure):
_fields_ = [('version', c_ushort),
('length',  c_ushort),
('id',      c_char * 8 ),
('key',     c_char * 32),
('auth',    c_char * 4)]
def __init__(self):
pass
def make(self, tp): # in tuple
self.version, self.length, self.id, self.key, self.auth = tp
def print_values(self):
print self.version, self.length, self.id, self.key, self.auth

사용 시
with open("MasterKey_V2.dat") as file:
data = file.read()
print type(data)
wk = WrappedKey()
wk.make(struct.unpack('HH8s32s4s', data[:48]))

C API 호출 시 

ret = CryptTest.mylib.UnWrapKey(
AI_AES_ECB,
AI_SHA1_HMAC,
szPBEKey,
len(szPBEKey),
byref(szRealKey),
byref(realKeyLen),
szKeyLabel,
byref(wk)
)

같은 식으로 하면 됩니다.

어느 분께는 도움이 되셨기를...




덧글|덧글 쓰기|신고