본문 바로가기
게임해킹/파이썬 해킹 프로그래밍

1장 개발 환경 구축

by HHack 2024. 12. 27.
반응형

1.1 파이썬 설치

현재 책에서는 2.5버전으로 나와있고 이클립스를 이용하고 있지만 난 파이참을 활용하도록 하겠다.

https://blog.naver.com/PostView.naver?blogId=h_shuue&logNo=223308521930&categoryNo=72&parentCategoryNo=72&from=thumbnailList

 

파이참 설치와 시작하기, PyCharm Visual Studio VS Code 비교, 파이썬 특화 IDE

간단한 프로그램 구현은 주피터 노트북으로 무리 없이 테스트 해 보고 있었지만 작성한 프로그램 수와 규모...

blog.naver.com

설치 방법은 위 블로그를 참조하면 된다.

그림 1.1 설치완료

1.2 라이브러리

1.2.1 ctypes

ctypes 라이브러리는 동적 링크 라이브러리 함수의 호출을 가능하게 하고, 복잡한 C 데이터 타입을 사용할 수 있게 하며 메모리를 관리하는 로우레벨 함수들을 제공한다.

1.2.2 동적 라이브러리

동적 링크 라이브러리(DLL)이란 컴파일된 바이너리로서 프로세스가 실행될 때 해당 프로세스에 동적으로 링크된다. 이는 외부에 익스포트 함수를 제공하며 익스포트 함수의 이름을 이용해 해당 함수의 실제 메모리상 주소를 구한다. 

  1. cdll() : 표준 cdecl 호출 규약을 이용하는 함수를 익스포트 하는 라이브러리를 로드하는데 사용한다.
  2. windll() : MS Win32 API 가 사용하는 stdcall 호출 규약을 이용하는 함수를 익스포트 하는 라이브러리를 로드하는데 사용한다.
  3. oledll() : windll() 방법과 동일하지만 익스포트 함수가 반환하는 값이 HRESULT라 가정한다. HRESULT는 MS 컴포넌트 객체 모델(COM)에서 에러 메시지를 반환하기 위해 특별히 사용되는 것이다.

1.2.3 printf()

C 런타임 함수인 printf()를 이용해 테스트 메시지를 출력해보도록 하겠습니다. 윈도우에서 C런타임 라이브러리는 msvcrt.dll 입니다.

from ctypes import *
import time

# msvcrt.dll 로드
msvcrt = cdll.msvcrt

# 바이트 문자열로 변환
message_string = b"Hello World!\n"

# 포맷 문자열도 바이트 문자열로 변환
msvcrt.printf(b"Testing : %s", message_string)

책에 나와있는 코드를 그대로 쓰면 오류가 발생합니다. 그 이유는 Python 3 버전에서의 문자열 처리 방식과 관련이 있습니다. ctypes를 사용할 때 C 함수는 바이트 문자열(char*)을 기대하지만, Python 3.x의 문자열은 기본적으로 유니코드(str)입니다. 이로 인해 첫 번째 문자만 처리되거나 잘못된 결과가 나올 수 있습니다.

그림 1.2 printf() 결과

※ 함수 호출 규약

https://hhack.tistory.com/38

 

10장 함수 호출 규약

10.1 함수 호출 규약함수 호출 규약(Calling Convention) : "함수를 호출할 때 파라미터를 어떤 식으로 전달하는가?"에 대한 규약스택이란 프로세스에서 정의된 메모리 공간이며 PE헤더에 그 크기가 명

hhack.tistory.com

위 게시물을 참고하자.

1.2.3 C 데이터 타입

C 타입 파이썬 타입 ctypes
char 한 문자 c_char
wchar_t 유니코드 한 문자 c_wchar
char int / long c_byte
unsigned char int / long c_ubyte
short int / long c_short
unsigned short int / long c_ushort
int int / long c_int
unsigned int int / long c_uint
long int / long c_long
unsigned long int / long c_ulong
long long int / long c_longlong
unsigned long long int / long c_ulonglong
float float c_float
double float c_double
char * (NULL terminated) string 또는 None c_char_p
wchar_t * (NULL terminated) unicode 또는 None c_wchar_p
void * int/long 또는 None c_void_p

1.2.4 레퍼런스를 통한 파라미터 전달

C와 C++ 에서는 함수의 파라미터로 포인터를 전달하는 것이 일상적이다. ctypes에서 포인터를 함수의 파라미터로 전달하려면 byref() 함수를 사용한다. 즉, function_main(byref(parameter))와 같은 형태로 호출하면 된다.

[C언어]

#include <stdio.h>

void increment(int* value) {
    *value += 1;
}
[Python]

from ctypes import *

# Windows에서는 msvcrt.dll이나 사용자가 만든 .dll 파일 로드
libc = CDLL("libexample.dll")  # C 코드가 컴파일된 DLL 파일

# C 함수의 파라미터 타입 설정
libc.increment.argtypes = [POINTER(c_int)]
libc.increment.restype = None

# 파이썬에서 C 타입 변수 생성
value = c_int(10)

# C 함수 호출 시 byref() 사용
libc.increment(byref(value))

# 결과 출력
print(value.value)  # 출력: 11

1.2.5 구조체와 유니언 정의

구조체와 유니언은 Win32 API 뿐만 아니라 리눅스의 libc에서 자주 사용되는 중요한 데이터 타입이다.

(1) 구조체

구조체는 단순히 동일하거나 서로 다른 데이터 타입의 변수들을 모아놓은 것이다. 구조체의 어느 한 멤버 변수에 접근하고자 할 때는 beer_recipe.amt_barley 처럼 '.'을 이용한다. 즉, beer_recipe.amt_barley는 beer_recipe 구조체의 amt_barley 멤버 변수에 접근한다.

[C언어]

struct beer_recipe {
    int amt_barley;
    int amt_water;
}
[Python]

from ctypes import *

class beer_recipt(Structure):
    _fields_ = [
    ("amt_barley", c_int),
    ("amt_water", c_int),
]

(2) 유니언

유니언은 구조체와 매우 유사하다. 하지만 유니언에서는 모든 멤버 변수가 동일한 메모리 공간을 공유한다. 따라서 유니언에서는 서로 다른 데이터 타입에 동일한 값을 저장하는 것이 가능하다.

[C언어]

union {
    long barley_long;
    int barley_int;
    char barley_char[8];
}barldy_amount;
[Python]

from ctypes import *

class barley_amount(Union):
    _fields_ = [
        ("barley_long", c_long),       # long 타입 필드
        ("barley_int", c_int),         # int 타입 필드
        ("barley_char", c_char * 8),   # char 배열 (길이 8)
    ]

barley_aount 유니언의 barley_int 멤버 변수에 66을 할당했다면 다른 멤버 변수인 barley_char를 이용해 그 값에 해당하는 문자를 출력할 수 있다.

from ctypes import *

class barley_amount(Union):
    _fields_ = [
        ("barley_long", c_long),       # long 타입 필드
        ("barley_int", c_int),         # int 타입 필드
        ("barley_char", c_char * 8),   # char 배열 (길이 8)
    ]

# 사용자 입력 받기 (Python 3.x에서는 input() 사용)
value = input("Enter the amount of barley to put into the beer vat: ")

# 입력 값을 정수로 변환하여 BarleyAmount 구조체 생성
my_barley = barley_amount(int(value))

# 결과 출력 (f-string 사용)
print(f"Barley amount as a long: {my_barley.barley_long}")
print(f"Barley amount as an int: {my_barley.barley_int}")
print(f"Barley amount as a char: {my_barley.barley_char.decode('utf-8', errors='ignore')}")

그림 1.3 union 예제 결과

보는 바와 같이 유니온에서는 하나의 값을 할당해 그 값을 세가지 형태로 표현하는 것이 가능하다. barley_char 변수의 출력값이 B인 이유는 숫자 66에 해당하는 ASCII 값이 B이기 때문이다.

barley_char 멤버 변수 정의를 보면 ctypes에서 배열을 어떻게 정의하는지 확실히 알 수 있다. ctypes에서는 배열에 할당하고자 하는 배열 요소의 개수를 해당 배열 요소 타입에 곱하는 형태로 배열을 정의한다. 따라서 barley_char 멤버 변수는 8개의 요소를 가지는 문자 배열로 정의된 것이다.

반응형

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

3장 윈도우 디버거 개발(1) - 디버기  (0) 2025.01.31
2장 디버거  (0) 2024.12.29