프로그래밍/동방프로젝트 봇 만들기

3. 피탄봄 자동화 구현

hideh 2025. 2. 16. 22:29

내가 리버싱 ctf를 하는건지 거참

 

피탄봄을 구현하기 위해 다음과 같은 과정을 생각하였다.

  1. 피탄 시 실행되어 잔기를 감소하도록 하는 함수를 찾는다.
  2. 피탄 유예 시간동안 실행되는 함수를 찾는다.
  3. 피탄 유예 시간을 저장하는 변수를 찾는다.
  4. 해당 변수값의 변화를 이용해 피탄 여부 판단 후 봄을 사용하도록 구현한다.

step 1

앞에서 주솟값 0069d4b8(+29d4b8 ) 의 메모리에는 잔기값이 들어간다는 것을 확인하였다. 따라서, 이 값을 수정하는 함수를 먼저 찾아보자.

친절하게도 치트엔진은 "여기 뭔가 쓰려고 오는 놈"들을 찾아주는 기능을 지원한다. 저걸 설정한 후 피탄당하면 다음과 같은 결과를 얻을 수 있다.

step 2

위의 결과에 따라, 00428DED 주소로 가면 아래 사진과 같이 00428DE6부터 살고있는 함수라는 것을 알 수 있다. 또한 바로 윗집에 사는 함수로부터 호출되는 것을 알 수 있다.

윗집 함수의 가장 처음으로 가면, 아래와 같이 더 높은 상사를 찾아갈 수 있다.

차례차례 쭉 올라온 결과 다음과 같은 함수를 찾을 수 있다.

이 함수는 unconditional이면서 피탄시 저 함수들을 호출하는 것을 보면, 게임을 하는 동안 계속해서 실행되는 함수라고 생각할 수 있다.

따라서, 우리는 +28C48 함수를 실행하는 조건은 바로 위에서 보이는, [eax+0x0009d8] 에 저장된 값이 0이 되는 것임을 알 수 있다.

step 3

이제 eax에 저장된 값을 찾으면 된다. 이를 위해, 다시 +28A8B에 breakpoint를 걸고 피탄당하면 다음과 같은 결과를 얻을 수 있다.

기대한대로 피탄 유예시간은 끝난 상태로, 피탄 애니메이션이 출력되기 전에 break가 걸렸다. 이때 eax값은 오른쪽 위의 registers에서 볼 수 있듯 006CA628로 나온다. 따라서, 0x6CA628+0x0009d8 = 0x6CB000 주소에 피탄 타이머가 있을 거라고 기대할 수 있다. 실제로 해당 주소로 가면 다음과 같다.

이 사진에서 볼 수 있듯, 현재 06이라는 값이 저장되어 있다.

실제 피탄을 당했을 때, 타이머가 줄어드는 것을 보면 맞는 것 같다. 이제 이 값을 이용하면 피탄봄을 만들 수 있다....!

step 4

그래서 해당 값이 6보다 작은 경우에 x를 누르도록 하면 될 것 같았으나.. 피탄봄 사용 이후 봄을 난사하는 것을 볼 수 있었다. 그 이유는 아래와 같이 유예 프레임이 '피탄 이후 봄 작동까지의 소요 프레임만큼 감소' 하기 때문에, 피탄봄에 성공하면 이후 6CB000의 값이 감소한다.

따라서 해당 부분을 반영해서, 다음과 같은 코드를 작성해 줄 수 있다.

import time
import keyboard 
import pymem 
import os


freq = 1/120  # Execution interval in seconds

def read_memory(pm, address):
    return pm.read_int(address)

def press_and_hold(key):
    keyboard.press(key)

def release_key(key):
    keyboard.release(key)

def auto_control():
    pm = pymem.Pymem("th6.exe")

    frame = 0
    remaind = 6
    try:
        while True:
            player_state = read_memory(pm, 0x006CB000) 
            
            if  player_state <remaind:
                    keyboard.press("x")  
                    time.sleep(freq*6)
                    keyboard.release("x")
                    print("피탄!")
                    time.sleep(24*freq)


                    if player_state == 0 :
                        remaind = 6
                    else:
                        remaind = player_state

            if frame == 0:
                press_and_hold("z")  
            frame = (frame + 1) % 60
            p1 = read_memory(pm, 0x006CAA68) 
            p2 = read_memory(pm, 0x006CB044) 
            print( p1 , p2)

            if p2 < p1:
                keyboard.press("left")
                time.sleep(freq/2)
                if frame == 0: print("[left]")
                keyboard.release("left")
            elif p2 > p1:
                keyboard.press("right")
                time.sleep(freq/2)
                if frame == 0:print("[right]")
                keyboard.release("right")
    except Exception as e:
        print(f"Memory read error: {e}")
    finally:
        release_key("z")  

if __name__ == "__main__":
    auto_control()

 

실행 영상은 다음과 같다.

 

 

'프로그래밍 > 동방프로젝트 봇 만들기' 카테고리의 다른 글

5. 탄막 위치 가져오기 (2)  (0) 2025.02.20
4. 탄막 위치 가져오기 (1)  (0) 2025.02.20
2. 조작 기능 구현  (0) 2025.02.16
1. 변수 위치 찾기(1)  (0) 2025.01.22
동방프로젝트 ai만들기  (0) 2025.01.21