[TUTORIAL] Aprendiendo Auto Assembler(AA) desde Cero ~Noob Friendly~

  • Hola Invitado ¿Quieres conversar con todos los usuarios de GamerzHacking?, No esperes mas y entra al canal de Discord dando clic AQUI
  • Hola Invitado ¿Tienes una Web y quieres ser partner de GamerzHacking?, No esperes mas y entra al siguiente enlace AQUI
  • Hola Invitado ¿Quieres formar parte del Staff de GamerzHacking?, No esperes mas y entra al siguiente enlace AQUI
  • Hola Invitado ¿Eres programador y quieres pertenercer GamerzHacking?, No esperes mas y entra a postular aqui AQUI
1 dAmerica/Bogota Noviembre dAmerica/Bogota 2015
1.063
175
63
27
Lima
gamerzhacking.com
#1
Hola amigos de GamerzHacking en esta oportunidad les redactare un tutorial sobre como aprender ensamblador automático, de una manera mas dinámica en cuestión de texto, debido a que ha pasado muchos años que no he redactado algo tan minucioso como lo plasmare aquí.

Indice

1. Introducción
2. Registros
2.1 Registros en x16 bits​
2.2 Registros en x86 bits​
2.3 Registros en x64 bits​
3. Instrucciones
3.1 Saltos condicionales​
3.2 Mov​
3.3 Push, Pop y la Pila(Stack)​
3.4 Alloc, Label y RegisterSymbol​
3.5 Call y Ret​
3.6 Otros​
-------------------------------------
1. Introducción
-------------------------------------


Como toda persona curiosa, debo suponer que esta aquí leyendo este tutorial haciendose muchas preguntas, una de ellas puedes ser.

#!Quiero aprender Auto Assembler, así que quiero empezar por aquí! (No pienses que te diré sopenco, bobo, noob, bueno para nada, nadie nace aprendiendo y vamos por un camino donde intentamos aprender cada día y pues por algún lado debemos de empezar ¿cierto?).

o

#Deseas poner a prueba tu conocimiento sobre Auto Assembler

Ahora si el primero es lo que tu piensas, lee lo que citare a continuación y escrito por Dark Byte creador del debugger Cheat Engine.

Dark Byte dijo:
Muchos creen que aprender Auto Assembler (AA) es difícil, pero de hecho es muy fácil
Si el segundo es cierto, pues no tengo ningún consejo que brindarte, sin embargo si observas dentro de este tutorial una información incorrecta o piensas que debería de editarlo y corregirlo, házmelo saber. Estoy en proceso de aprendizaje también.

Si te preguntas: ¿Porque debo se hacerle caso a alguien que esta aprendiendo tambien?
Aunque este aprendiendo sobre Auto Assembler, se que lo poco que puedo compartir te ayudara a comprender mas sobre el código maquina automático.

Comencemos....

-------------------------------------
2. Registros
-------------------------------------


Los registros son elementos de almacenamiento de datos contenidos en el procesador y que tienen la ventaja de la rapidez de acceso y la finalidad de contener datos necesarios para la ejecución del programa. Aunque tiene distinto nombre cada uno de ellos, cuentan básicamente con la misma funcionalidad, con algunas excepciones. Determinadas operaciones -por ejemplo la multiplicación- exigen que los operandos estén en registros específicos.

2.1 Registros de x64 bits

Primero que nada, te explicare que función cumple cada registro y el nombre que se le asigna, la R esta delante (si observas a continuación, todos los registros de 64 bits empiezan con un R) te indica que es un registro de 64 bits. Las letras A,B,C y D deberían volverse muy obvias después de leer la descripción de cada registro. Igual que SI,DI,BP,SP e IP. La X después del RAX, RBX, RCX y RDX.

RAX: Registro Acumulador.- Se usa para almacenar el resultado de las operaciones, es al único registro con el que se puede hacer divisiones y multiplicaciones.
RBX: Registro Base.- Almacena la dirección base para los accesos a memoria.
RCX: Registro Contador.- Actúa como contador en los bucles de repetición.
RDX: Registro de Datos.- Es usado para almacenar los datos de las operaciones.
RSI: Registro de Indice de Origen.- Almacena el desplazamiento del operando de origen en memoria en algunos tipos de operaciones (operaciones con operandos en memoria).
RDI: Registro de Indice de Destino.- almacena el desplazamiento del operando de destino en memoria en algunos tipos de operaciones (operaciones con operandos en memoria).
RBP: Registro Indice de la Pila.- Almacena el desplazamiento dentro del segmento de pila, y apunta al último elemento introducido en la pila.
RSP: Registro Indice de Base.- Se usa para almacenar desplazamiento en los distintos segmentos. Por defecto es el segmento de la pila.

2.2 Registros de x86 bits

Los registros de x86 bits son muy similares a los registros de x64 bits, a diferencia que en ves de indicar con la R, empiezan con la letra E.

EAX: ver RAX
EBX: ver RBX
ECX: ver RCX
EDX: ver RDX
ESI: ver RSI
EDI: ver RDI
EBP: ver RBP
ESP: ver RSP

2.3 Registros de x16 bits

Los registros de x16 bits son muy similares a los registros de x86 y x64 bits, diferencia que ellos no inician con ninguna letra.

AX: ver RAX
BX: ver RBX
CX: ver RCX
DX: ver RDX
SI: ver RSI
DI: ver RDI
BP: ver RBP
SP: ver RSP

La información brindada es mas o menos de lo que que entiendo o se sobre los registros, si quieres mas información puedes consultar con GOOGLE.

-------------------------------------
3. Instrucciones
-------------------------------------


Miremos esta instruccion

Código:
jmp 004582AB -> opcode
Te debo contar algunas cosas que necesitas saber sobre opcodes.

Siempre hay una direcion y/o registro involucrado en un codigo de operacion, asi como la instruccion vista arriba. La direcion esta en HEX, que es la abreviatura de hexadecimal. Hexadecimal es un sistema de numeracion en base 16. Es como un sistema de numeracion decimal, que tiene una base de 10.

Imaginemos de la siguiente forma, si te encantan las matematicas debes saber que nuestro sistema de numeracion normal es decimal, como habia indicado es en base 10. Esto significa que no podemos tener un 10 en una cloumna, sino que se coloca en dos. En hexadecimal, puede tener una caloumna de 10 en uno, puede tener hasta 15 en una cloumna. Pero tal vez te preguntas sobre el hecho de que 1 y 0 todavia no encajan en alguna columna.

Relajate, en decimal el numero 10 se reemplazar por A en Hexadecimal, el numero 11 en decimal se reemplaza por B en hexadecimal, y asi sucesivamente hasta F, que significa 16. Despues de eso en 10, luego 11, hasta 1F, y luego comienza de nuevo con los 20, y asi sucesivamente. Una forma de comprobar lo que hago entender es que utilizar tu calculadora en modo programador y lo veas por ti mismo.

Cada dirección tiene un código de operación y cantidad de bytes. El numero de bytes corresponde a lo que es el código de operación, ya que cada instrucción ocupa un cierto numero de bytes. Para finalizar lo mas importante es el "comentar" algo dentro de nuestra instrucción, y que solo requiere colocar "//", luego escribes lo que deseas comentar.

3.1 Saltos Condicionales

El listado de un programa consiste en una sucesión de instrucciones. Sin embargo a la hora de ejecutarlo, la ejecución del mismo no sigue el orden del listado, sino que, de acuerdo con distintas circunstancias y mediante las instrucciones de salto, se interrumpe la ejecución lineal del programa para continuar dicha ejecución en otro lugar del código.

Las instrucciones de salto son básicamente de dos tipos: de salto condicional y de salto incondicional. En el primer tipo, la instrucción de salto suele ponerse después de una comparación y el programa decide si se efectúa o no el salto, según el estado de los flags (excepto en un caso, en el que el salto se efectúa si el valor del registro ECX o CX es cero). En el segundo, el salto se efectúa siempre(JMP es ese salto que siempre se efectúa).



3.2 Mov (Mover)

Como dice el subtitulo MOV (Mover), cuando veas una instrucción que inicie con MOV es porque esta realizando un movimiento de un valor que se almacena un registro a otro registro.

Ejemplo 1

Código:
mov eax, ebx
Lo que significa nuestra instrucción que acabamos de colocar es "mover la dirección que almacena el registro EBX al registro EAX". Observa que hay una coma "," que separa ambos registros, y no un espacio. Puedes verlo un poco difícil al comienzo, pero es muy sencillo.

Como había indicado antes MOV significa "Mover" y también puedes interpretarlo de la siguiente forma "copiar la dirección del registro EBX y pegarlo en el registro EAX" y sobrescribiera la dirección que estaba antes en EAX para así pegar la dirección que se esta copiando del registro EBX.

Ejemplo 2

Código:
mov eax, [ebx]
De igual manera realiza lo mismo "mover el valor de EBX a la dirección que almacena dentro del registro EAX". Es decir lo que se encuentra dentro de [corchetes] ya sea un registro o dirección significa el VALOR que se encuentro dentro de ello.

Pero lo que si no funcionara es lo siguiente.

Ejemplo 3

Código:
mov [eax], [ebx]
Repite conmigo para que así se te quede dentro de la cabecita tuya, dicha instrucción NO TRABAJA NO FUNCIONARA hasta que se te quede grabado. NO PUEDES mover el valor de una cosa al valor de otra cosa. Pero no tomes esto de una manera equivocada: puedes mover el valor de una cosa indirectamente a otra cosa, veamos el siguiente código.

Código:
push eax // Empujar EAX a la pila (stack) - Lo veremos en el siguiente tema
mov eax, [0100200A] // Moveremos el valor de la direccion 0100200A al registro eax
mov [ebx], eax // Mover eax (que es el valor de 0100200A) en el valor de ebx
pop eax // Pop eax de la pila (stack) - Lo veremos en el siguiente tema
Creo que es todo lo que debes saber sobre MOV.

3.3 Push, Pop y la Pila(Stack)

Bueno viste en el tema anterior como utilizamos el Push y Pop, debo suponer que te debes estar realizando las siguientes preguntas.

¿Que es PUSH? ¿Que es POP? ¿Que es la Pila (Stack)?
Veamos que es PUSH...

Basicamente lo que realiza esta instruccion es poner EAX en la pila (Stack).

Veamos que es POP...

Es todo lo contrario de PUSH, es decir que quita lo que se puso en la pila (Stack).

Veamos que es la PILA (Stack)...

Les contare un pequeño ejemplo muy dinamico que entenderan de forma muy facil la funcion de la Pila.

La pila se usa para dar a la variable un valor en blanco y usarla para el almacenamiento. Es decir: Tiene un papel en la cual estas realizando tus tareas, pero en ese momento te llama un amigo para decirte que tienes que llamar a EuPoder, donde su numero telefónico es 12345678. De pronto te entra el pánico, que no tienes donde anotarlo, así que decides escribirlo en tu hoja de tareas. Después que terminas de hablar con tu amigo, tendrás ahora el tiempo para encontrar tu agenda telefónica para poder escribir el numero de EuPoder. Después de anotar el numero de EuPoder en tu agenda telefónica, borraras el numero que anotaste en tu hoja de tareas. Llama a EuPoder y continua con tus tareas.

Push(Empujar) a un valor en la pila es "anotar el numero de EuPower en la hoja de tareas". Hacer Pop(Quitar) es "anotarlo en la agenda telefónica, luego borrarlo de la hoja de tareas".
3.4 Alloc, Label y RegisterSymbol

Vamos a clasificar los scripts en 2 tipos

a) Simples de 1 direccion

El siguiente código que verán es un ejemplo de un código simple.

Código:
[enable]
00ABC123:
mov eax, ebx
[disable]
00ABC123:
mov ebx, eax
Todo lo que esta realizando es cambiar la parte del código de la dirección 00ABC123.

b) Complejos de 2 o mas direcciones

Ejemplo:

Código:
[Enable]
Alloc(Hook, 124)
Alloc(HP, 4)
Alloc(MP, 4)
Label(Return)
Label(UseHP)
Label(UseMP)
Label(End)
define(HP,#%arg1)
define(MP,#%arg2)
define(HealthKey, %arg3)
define(ManaKey, %arg4)
Hook:
mov eax,[033A6C48] // CWvsContext 8B 0D ? ? ? ? 50 E8 ? ? ? ? 8D ? ? E8 ? ? ? ? 8B
mov eax,[eax+2260] // ZRef<CharacterDat a> 8D ? ? 53 56 57 50 E8 [Follow call]
push esi
// Check HP
mov esi,[eax+5A] // _ZtlSecureTear_nHP[1]
rol esi,05
xor esi,[eax+56] // _ZtlSecureTear_nHP[0]
cmp esi, HP // HP Value
jle UseHP
// Check MP
mov esi,[eax+72] // _ZtlSecureTear_nMP[1]
rol esi, 05
xor esi,[eax+6E] // _ZtlSecureTear_nMP[0]
cmp esi, MP // MP Value
jle UseMP
jmp End
UseHP:
pushad
mov ebx,HealthKey // Change Key For HP
call PressKey
popad
jmp End
UseMP:
pushad
mov ebx,ManaKey // Change Key For MP
call PressKey
popad
jmp End
End:
pop esi
push ebp // Original Opcode
mov ebp,esp // Original Opcode
push -01 // Original Opcode
jmp Return
PressKey:
mov esi,[033A6C48] // TSingletonCWvsContext 8B 0D ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? 8D 4D ?? E8 ?? ?? ?? ?? 8B 4D ?? 64 89 0D ?? ?? ?? ?? 59
mov ecx,[esi+A4]
push ebx
push 00
call 027225E0 // CWndMan::OnKey 55 8B EC 8B 0D ?? ?? ?? ?? 85 C9 74 ?? 83 C1 ?? [First]
ret
011C4960: // CField::Update
jmp Hook
Return:
[Disable]
DeAlloc(Hook)
DeAlloc(HP)
DeAlloc(MP)
011C4960: //[START] 3D B8 0B 00 00 76 ? 6A
push ebp
mov ebp,esp
push -01

En el ejemplo del script en AA hemos visto algunas lineas como Alloc, Label o RegisterSymbol, de la cual vamos a explicar a continuacion.

Label

La funcion que realiza LABEL es permitir que la variable se use como una secuencia de instrucciones, por ejemplo:

Código:
label(seguir)

gamerzhacking:
mov eax,02
cmp eax,03
jne seguir
je Nelson

seguir:
//...
//...

Nelson:
jmp 0 // Da crash al instante
Hay algunas cosas que debes tener en cuenta en el script. Una de ellas es que le falta la funcion de "Alloc", que se indicara para que sirve a continuacion.

Si hasta el momento has entendi porque coloque el salto condicional JNE, estas yendo por un buen camino.

Alloc

Ahora la funcion que realiza ALLOC es asignar X cantidad de memoria a su proposito. Ahora, esta memoria es cualquier memoria antigua, esta es memoria no utilizada, que puede sobreescribir sin tener problemas. Como te indique, Alloc asigna X cantidad de memoria. X es una variable que contiene cierta cantidad de bytes (1024 bytes o 1 kb) suele ser suficiente para lo que estamos intentando lograr. Ahora con esto ya sabemos que funcion realiza ALLOC en nuestro script, veamos un ejemplo.

Código:
alloc(GamerzHacking, 1024)
alloc(Nelson, 1024)
label(Seguir)

GamerzHacking:
mov eax, 02
cmp eax, 03
jne Seguir
je Nelson

Seguir:
//...

Nelson:
jmp 0
Observa, lo que no hago!!!

Código:
label(GamerzHacking)
o

Código:
label(Nelson)
Porque ya lo tenemos definido dentro de CheatEngine.

RegisterSymbol

La funcion de "registersymbol"es agregar un símbolo a la lista de símbolos definida por el usuario para que las tablas de cheats y el memory browser puedan usar ese nombre en lugar de una dirección.

Código:
alloc(GamerzHacking, 1024)
alloc(Nelson, 1024)
alloc(GZH, 1024)
label(Seguir)
registersymbol(GZH)

GamerzHacking:
mov eax, 02
cmp eax, 03
jne Seguir
je Nelson

Seguir:
cmp eax, [GZH]

Nelson:
jmp 0
ContraPartes

Ahora la ultima parte de este mini script veremos unas contrapartes. Hay dos cosas que puede hacer con una secuencia de comandos Auto Assembler.

1. Puedes inyectar
2. Puedes agregar a tu Cheat Table

Si decides que quieres agregar a tu Cheat Table (que es lo que la mayoría de los script son hoy en día), entonces necesitas una sección de ENABLE y DISABLE en tu código, como veras a continuación.

C++:
[ENABLE]
alloc(GamerzHacking, 1024)
alloc(Nelson, 1024)
alloc(GZH, 1024)
label(Seguir)
registersymbol(GZH)

GamerzHacking:
mov eax, 02
cmp eax, 03
jne Seguir
je Nelson

Seguir:
cmp eax, [GZH]

[DISABLE]
Ahora como habrás observado, no hay nada en la seccion DISABLE, lo que deseo lograr en esta sección es deshacer lo que ha hecho en la sección de ENABLE.

Aqui viene la siguiente pregunta ¿Como hacer para deshacer el Alloc y el Registersymbol?

Es sencillo lo que haremos a continuacion, y lo que pondremos sera solo un "dealloc" y anulara la funcion de registersymbol. Significa que en la seccion DISABLE se eliminara casi la mitad del codigo de la seccion del ENABLE.

"No entiendo ¿Como asi?"

Veamos el siguiente script.

C++:
[ENABLE]
alloc(GamerzHacking, 1024)
alloc(Nelson, 1024)
alloc(GZH, 1024)
label(Seguir)
registersymbol(GZH)

GamerzHacking:
mov eax, 02
cmp eax, 03
jne Seguir
je Nelson

Seguir:
cmp eax, [GZH]

[DISABLE]
alloc(GamerzHacking)
alloc(Nelson)
alloc(GZH)
unregistersymbol(GZH)
"Espera Nelson, y que paso con el valor 1024 que asignaste a la memoria"

Perdón me olvidaba, dado que el ordenador sabe que se asigno 1024 bytes a GamerzHacking, solo necesitar desasignar GamerzHacking, y sabra que desasignara 1024 bytes que se asignaron a GamerzHacking (Perdonen si he sido algo redundante).

3.5 Call y Ret

La funcion CALL es muy similar a la funcion JMP. La unica diferencia es que tiene una contraparte para volver a donde estabas antes. Veamos un script simple que utiliza estas dos funciones.

C#:
mov [0100579C],10 //Mover 10 al valor de la direccion
cmp [0100579C],0  // Compara con 0
call Nelson  //Llamar o Saltar a Nelson
jmp 01002FF5
                   
Nelson:
dec [0100579C]     //Disminuir el valor de la direccion
cmp [0100579C],0   // Compara con 0
jne Nelson   // Si no es igual, saltar de nuevo al comienzo de
                      //Nelson, de lo contrario continuar
ret                 //Regresar al código anterior, justo después de la función call
Info
Continuaraaaa

Warning
Continuaraaaa

Help
Continuaraaaa

Edit: Continuaraaaa

Important
Continuaraaaa

Offtopic:
Continuaraaaa
 
Última modificación: