Browse Source

update for new version

Matthew Trejo 4 months ago
parent
commit
a677409c5f
5 changed files with 208 additions and 26 deletions
  1. 21 6
      README.md
  2. 17 5
      config.py
  3. 77 11
      patcher.py
  4. 39 3
      patches/patch_factory.py
  5. 54 1
      patterns/pattern_searcher.py

+ 21 - 6
README.md

@@ -1,6 +1,6 @@
-# Patcher Avanzado de Stamina para PEAK.exe
+# Patcher Avanzado para PEAK.exe
 
-Un patcher de memoria avanzado que utiliza patrones de diseño para modificar el comportamiento del stamina en el juego PEAK.exe.
+Un patcher de memoria avanzado que utiliza patrones de diseño para modificar el comportamiento del stamina y la velocidad de movimiento en el juego PEAK.exe.
 
 ## 🏗️ Arquitectura del Proyecto
 
@@ -61,6 +61,9 @@ python main.py
 
 ## 📋 Características
 
+- ✅ **Stamina infinita**: Mantiene el stamina siempre al 100%
+- ✅ **Velocidad modificable**: Cambia la velocidad de movimiento (0.5x, 1x, 2x, 3x)
+- ✅ **Menú interactivo**: Selección fácil del tipo de parche a aplicar
 - ✅ **Búsqueda optimizada**: Chunks de 128KB con overlap inteligente
 - ✅ **Backup automático**: Copia de seguridad del código original
 - ✅ **Verificación**: Confirma que el parche se aplicó correctamente
@@ -95,6 +98,22 @@ _patch_types = {
 2. **Implementar métodos abstractos** `get_patterns()` y `search()`
 3. **Usar en el patcher principal**
 
+### Funcionalidades Implementadas
+
+#### Stamina Infinita
+- Busca el patrón específico de stamina en memoria
+- Modifica el código para mantener el stamina siempre al 100%
+
+#### Velocidad de Movimiento
+- Busca el patrón específico de velocidad en memoria
+- Permite modificar la velocidad con diferentes multiplicadores:
+  - **0.5x**: Media velocidad
+  - **1x**: Velocidad normal
+  - **2x**: Doble velocidad
+  - **3x**: Triple velocidad
+- Patrón: *pendiente*
+- Modificación: Cambia los bytes `C8 41` por el valor correspondiente al multiplicador
+
 ## ⚠️ Advertencias
 
 - **Uso bajo tu propia responsabilidad**
@@ -102,10 +121,6 @@ _patch_types = {
 - **Crear backup antes de usar**
 - **Solo para uso educativo/personal**
 
-## 🔄 Migración desde Versión Anterior
-
-El archivo `advanced_patcher.py` original se mantiene para compatibilidad. La nueva versión modular está en `main.py`.
-
 ## 📝 Notas de Desarrollo
 
 - **Configuración centralizada** en `config.py`

+ 17 - 5
config.py

@@ -33,16 +33,26 @@ PATCH_CONFIG = {
             0xB8, 0x00, 0x00, 0x80, 0x3F,        # mov eax, 0x3F800000 (1.0f)
             0x66, 0x0F, 0x6E, 0xE8,              # movd xmm5, eax
             0x90, 0x90, 0x90, 0x90, 0x90,        # nop padding
-            0xF3, 0x0F, 0x11, 0xAE, 0x8C, 0x01, 0x00, 0x00  # movss [rsi+18C],xmm5
+            0xF3, 0x0F, 0x11, 0xAE, 0x94, 0x01, 0x00, 0x00  # movss [rsi+18C],xmm5
         ]
+    },
+    'speed': {
+        'description': "Parche de velocidad de movimiento - modifica la velocidad del jugador",
+        'original_pattern': [0xE0, 0x67, 0x03, 0x36, 0x7B, 0x02, 0x00, 0x00, 0x00, 0x00, 0xC8, 0x41, 0x00, 0x00, 0x80, 0x3F],
+        'speed_multipliers': {
+            '1x': [0xE0, 0x67, 0x03, 0x36, 0x7B, 0x02, 0x00, 0x00, 0x00, 0x00, 0xC8, 0x41, 0x00, 0x00, 0x80, 0x3F],  # Velocidad normal
+            '2x': [0xE0, 0x67, 0x03, 0x36, 0x7B, 0x02, 0x00, 0x00, 0x00, 0x00, 0x48, 0x42, 0x00, 0x00, 0x80, 0x3F],  # Doble velocidad
+            '3x': [0xE0, 0x67, 0x03, 0x36, 0x7B, 0x02, 0x00, 0x00, 0x00, 0x00, 0x96, 0x42, 0x00, 0x00, 0x80, 0x3F],  # Triple velocidad
+            '0.5x': [0xE0, 0x67, 0x03, 0x36, 0x7B, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40, 0x41, 0x00, 0x00, 0x80, 0x3F]  # Media velocidad
+        }
     }
 }
 
 # Mensajes de la interfaz de usuario
 UI_MESSAGES = {
     'banner': {
-        'title': "🎮 === Patcher Avanzado de Stamina para PEAK.exe ===",
-        'description': "Este programa modifica la memoria para que el stamina siempre sea 100%",
+        'title': "🎮 === Patcher Avanzado para PEAK.exe ===",
+        'description': "Este programa modifica la memoria para stamina infinita y velocidad de movimiento",
         'warning': "⚠️  ADVERTENCIA: Usa bajo tu propia responsabilidad",
         'admin_note': "📝 Asegúrate de ejecutar como administrador"
     },
@@ -52,12 +62,13 @@ UI_MESSAGES = {
         'verification_successful': "✅ Verificación exitosa: El parche se aplicó correctamente",
         'backup_saved': "💾 Copia de seguridad guardada en: {filename}",
         'patching_completed': "\n🎉 ¡Patcheo completado exitosamente!",
-        'stamina_maintained': "💪 El stamina ahora debería mantenerse al 100%"
+        'stamina_maintained': "💪 El stamina ahora debería mantenerse al 100%",
+        'speed_modified': "🏃 La velocidad de movimiento ha sido modificada"
     },
     'error': {
         'process_not_found': "❌ No se pudo encontrar el proceso {process_name}",
         'process_not_opened': "❌ No se pudo abrir el proceso {process_name}",
-        'no_patterns_found': "❌ No se encontraron patrones de stamina",
+        'no_patterns_found': "❌ No se encontraron patrones",
         'memory_read_failed': "❌ No se pudo leer la memoria original",
         'patch_failed': "❌ Error al aplicar el patch",
         'verification_failed': "❌ Error en la verificación - El patch no se aplicó correctamente",
@@ -72,6 +83,7 @@ UI_MESSAGES = {
         'pattern_found': "   ✅ {pattern_name} encontrado en: 0x{address:X}",
         'using_address': "\n🎯 Usando dirección: 0x{address:X}",
         'searching_stamina': "\n🔍 Buscando patrones de stamina...",
+        'searching_speed': "\n🔍 Buscando patrones de velocidad...",
         'original_code': "\n📖 Código original:",
         'patch_to_apply': "\n🔧 Patch a aplicar:",
         'applying_patch': "\n✍️ Aplicando patch...",

+ 77 - 11
patcher.py

@@ -5,7 +5,7 @@ import os
 sys.path.append(os.path.dirname(os.path.abspath(__file__)))
 from config import TARGET_PROCESS, UI_MESSAGES
 from core.memory_manager import MemoryManager
-from patterns.pattern_searcher import StaminaPatternSearcher
+from patterns.pattern_searcher import StaminaPatternSearcher, SpeedPatternSearcher
 from patches.patch_factory import PatchFactory
 from utils.helpers import HexDumper, BackupManager, UserInterface
 
@@ -14,24 +14,74 @@ class AdvancedPatcher:
     
     def __init__(self):
         self.memory_manager = MemoryManager()
-        self.pattern_searcher = StaminaPatternSearcher()
         self.patch_factory = PatchFactory()
         self.hex_dumper = HexDumper()
         self.backup_manager = BackupManager(self.memory_manager)
         self.ui = UserInterface()
         self.target_process = TARGET_PROCESS['name']
     
-    def apply_stamina_patch(self) -> bool:
-        """Aplica el parche de stamina usando todos los módulos"""
+    def show_patch_menu(self) -> tuple:
+        """Muestra el menú de selección de parches"""
+        print("\n🎯 Selecciona el tipo de parche a aplicar:")
+        print("1. Stamina infinita")
+        print("2. Velocidad de movimiento")
+        print("3. Salir")
+        
+        while True:
+            try:
+                choice = input("\n➤ Ingresa tu opción (1-3): ").strip()
+                if choice == '1':
+                    return ('stamina', None)
+                elif choice == '2':
+                    return self.show_speed_menu()
+                elif choice == '3':
+                    return (None, None)
+                else:
+                    print("❌ Opción inválida. Por favor ingresa 1, 2 o 3.")
+            except KeyboardInterrupt:
+                return (None, None)
+    
+    def show_speed_menu(self) -> tuple:
+        """Muestra el menú de selección de velocidad"""
+        multipliers = self.patch_factory.get_speed_multipliers()
+        print("\n🏃 Selecciona el multiplicador de velocidad:")
+        for i, mult in enumerate(multipliers, 1):
+            print(f"{i}. {mult}")
+        
+        while True:
+            try:
+                choice = input(f"\n➤ Ingresa tu opción (1-{len(multipliers)}): ").strip()
+                try:
+                    index = int(choice) - 1
+                    if 0 <= index < len(multipliers):
+                        return ('speed', multipliers[index])
+                    else:
+                        print(f"❌ Opción inválida. Por favor ingresa un número entre 1 y {len(multipliers)}.")
+                except ValueError:
+                    print("❌ Por favor ingresa un número válido.")
+            except KeyboardInterrupt:
+                return (None, None)
+    
+    def apply_patch(self, patch_type: str, speed_multiplier: str = None) -> bool:
+        """Aplica el parche especificado usando todos los módulos"""
         # Buscar y abrir el proceso
         if not self.memory_manager.open_process(self.target_process):
             print(UI_MESSAGES['error']['process_not_found'].format(process_name=self.target_process))
             return False
-    
-        print(UI_MESSAGES['info']['searching_stamina'])
+        
+        # Seleccionar el buscador de patrones apropiado
+        if patch_type == 'stamina':
+            pattern_searcher = StaminaPatternSearcher()
+            print(UI_MESSAGES['info']['searching_stamina'])
+        elif patch_type == 'speed':
+            pattern_searcher = SpeedPatternSearcher()
+            print(UI_MESSAGES['info']['searching_speed'])
+        else:
+            print(f"❌ Tipo de parche no soportado: {patch_type}")
+            return False
         
         # Buscar patrones
-        found_addresses = self.pattern_searcher.search(self.memory_manager)
+        found_addresses = pattern_searcher.search(self.memory_manager)
         
         if not found_addresses:
             print(UI_MESSAGES['error']['no_patterns_found'])
@@ -44,11 +94,17 @@ class AdvancedPatcher:
         target_address = self.ui.select_target_address(found_addresses)
         
         # Crear el parche
-        patch = self.patch_factory.create_patch('stamina')
+        if patch_type == 'speed' and speed_multiplier:
+            patch = self.patch_factory.create_patch(patch_type, speed_multiplier=speed_multiplier)
+        else:
+            patch = self.patch_factory.create_patch(patch_type)
+            
         if not patch:
-            print("❌ No se pudo crear el parche de stamina")
+            print(f"❌ No se pudo crear el parche de {patch_type}")
             return False
         
+        print(f"\n📋 {patch.get_description()}")
+        
         # Leer código original
         original_size = patch.get_patch_size()
         original_code = self.memory_manager.read_memory(target_address, original_size)
@@ -102,10 +158,20 @@ class AdvancedPatcher:
         
         success = False
         try:
-            success = self.apply_stamina_patch()
+            # Mostrar menú de selección
+            patch_type, speed_multiplier = self.show_patch_menu()
+            
+            if patch_type is None:
+                print(UI_MESSAGES['info']['goodbye'])
+                return False
+            
+            success = self.apply_patch(patch_type, speed_multiplier)
             if success:
                 print(UI_MESSAGES['success']['patching_completed'])
-                print(UI_MESSAGES['success']['stamina_maintained'])
+                if patch_type == 'stamina':
+                    print(UI_MESSAGES['success']['stamina_maintained'])
+                elif patch_type == 'speed':
+                    print(UI_MESSAGES['success']['speed_modified'])
             else:
                 print(UI_MESSAGES['error']['patching_failed'])
         except Exception as e:

+ 39 - 3
patches/patch_factory.py

@@ -50,6 +50,32 @@ class StaminaPatch(Patch):
         """Descripción del parche de stamina"""
         return PATCH_CONFIG['stamina']['description']
 
+class SpeedPatch(Patch):
+    """Parche específico para velocidad de movimiento"""
+    
+    def __init__(self, speed_multiplier: str = '2x'):
+        self.speed_multiplier = speed_multiplier
+    
+    def create_patch(self, original_data: bytes) -> bytes:
+        """Crea el código de parche para velocidad modificada"""
+        # Obtener configuración del patch desde config
+        speed_config = PATCH_CONFIG['speed']
+        
+        # Obtener el patrón de velocidad deseado
+        if self.speed_multiplier in speed_config['speed_multipliers']:
+            return bytes(speed_config['speed_multipliers'][self.speed_multiplier])
+        else:
+            # Fallback a velocidad normal si no se encuentra el multiplicador
+            return bytes(speed_config['speed_multipliers']['1x'])
+    
+    def get_patch_size(self) -> int:
+        """Retorna el tamaño del parche de velocidad"""
+        return 16
+    
+    def get_description(self) -> str:
+        """Descripción del parche de velocidad"""
+        return f"{PATCH_CONFIG['speed']['description']} (Multiplicador: {self.speed_multiplier})"
+
 class HealthPatch(Patch):
     """Parche para vida infinita (ejemplo de extensibilidad)"""
     
@@ -69,18 +95,28 @@ class PatchFactory:
     
     _patch_types = {
         'stamina': StaminaPatch,
+        'speed': SpeedPatch,
         'health': HealthPatch
     }
     
     @classmethod
-    def create_patch(cls, patch_type: str) -> Optional[Patch]:
+    def create_patch(cls, patch_type: str, **kwargs) -> Optional[Patch]:
         """Crea un parche del tipo especificado"""
         patch_class = cls._patch_types.get(patch_type.lower())
         if patch_class:
-            return patch_class()
+            if patch_type.lower() == 'speed':
+                speed_multiplier = kwargs.get('speed_multiplier', '2x')
+                return patch_class(speed_multiplier)
+            else:
+                return patch_class()
         return None
     
     @classmethod
     def get_available_patches(cls) -> list:
         """Retorna la lista de parches disponibles"""
-        return list(cls._patch_types.keys())
+        return list(cls._patch_types.keys())
+    
+    @classmethod
+    def get_speed_multipliers(cls) -> list:
+        """Retorna la lista de multiplicadores de velocidad disponibles"""
+        return list(PATCH_CONFIG['speed']['speed_multipliers'].keys())

+ 54 - 1
patterns/pattern_searcher.py

@@ -45,7 +45,7 @@ class StaminaPatternSearcher(PatternSearcher):
             "stamina_pattern_1": (
                 bytes([0x75, 0x00, 0xF3, 0x0F, 0x10, 0x44, 0x24, 0x08, 
                       0xF3, 0x0F, 0x5A, 0xC0, 0xF2, 0x0F, 0x5A, 0xE8, 
-                      0xF3, 0x0F, 0x11, 0xAE, 0x8C, 0x01, 0x00, 0x00]),
+                      0xF3, 0x0F, 0x11, 0xAE, 0x94, 0x01, 0x00, 0x00]),
                 ['x', '?', 'x', 'x', 'x', 'x', 'x', 'x', 
                  'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 
                  'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x']
@@ -90,5 +90,58 @@ class StaminaPatternSearcher(PatternSearcher):
                             print(UI_MESSAGES['info']['pattern_found'].format(pattern_name=pattern_name, address=found_address))
                             print("   🛑 Deteniendo búsqueda - primer patrón encontrado")
                             return found_addresses
+
+class SpeedPatternSearcher(PatternSearcher):
+    """Buscador específico para patrones de velocidad"""
+    
+    def get_patterns(self) -> Dict[str, Tuple[bytes, List[str]]]:
+        """Define los patrones específicos para velocidad"""
+        return {
+            "speed_pattern_1": (
+                bytes([0xE0, 0x67, 0x03, 0x36, 0x7B, 0x02, 0x00, 0x00, 
+                      0x00, 0x00, 0xC8, 0x41, 0x00, 0x00, 0x80, 0x3F]),
+                ['x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 
+                 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x']
+            )
+        }
+    
+    def search(self, memory_manager: MemoryManager) -> List[Tuple[str, int]]:
+        """Busca patrones de velocidad en memoria"""
+        patterns = self.get_patterns()
+        regions = memory_manager.get_memory_regions()
+        found_addresses = []
+        
+        print(UI_MESSAGES['info']['searching_patterns'].format(region_count=len(regions)))
+        
+        # Pre-calcular longitudes de patrones
+        pattern_info = {name: (pattern, mask, len(pattern)) 
+                       for name, (pattern, mask) in patterns.items()}
+        
+        for region_idx, region in enumerate(regions):
+            print(f"   Región {region_idx + 1}/{len(regions)}: 0x{region['base']:X} - 0x{region['base'] + region['size']:X}")
+            
+            # Configuración optimizada de chunks desde config
+            chunk_size = MEMORY_SEARCH_CONFIG['chunk_size']
+            overlap = MEMORY_SEARCH_CONFIG['overlap']
+            
+            for offset in range(0, region['size'], chunk_size):
+                current_address = region['base'] + offset
+                read_size = min(chunk_size + overlap, region['size'] - offset)
+                
+                data = memory_manager.read_memory(current_address, read_size)
+                if not data:
+                    continue
+                
+                # Buscar cada patrón
+                data_len = len(data)
+                for pattern_name, (pattern, mask, pattern_len) in pattern_info.items():
+                    search_limit = data_len - pattern_len + 1
+                    for i in range(search_limit):
+                        if self.pattern_matches(data, pattern, mask, i):
+                            found_address = current_address + i
+                            found_addresses.append((pattern_name, found_address))
+                            print(UI_MESSAGES['info']['pattern_found'].format(pattern_name=pattern_name, address=found_address))
+                            print("   🛑 Deteniendo búsqueda - primer patrón encontrado")
+                            return found_addresses
         
         return found_addresses