Cómo es una demostración el propio programa se parchea así mismo, por lo que necesitamos saber en que lugar vamos a querer modificar las cosas.
Para eso en el programa puse un bloque condicional que es el que me interesa parchear para poder accederlo aunque no se cumpla la condición.
Por Ejemplo:
Código:
if( 0 == 1){ /* no se cumple */ }
Con un desensamblador, abro el ejecutable (el archivo EXE) y procedo a mirar el código ensamblador.
Identifico el OPCODE que me interesa cambiar, en este caso es JZ (0x74), y procedo a obtener la posición en donde se encuentra (dirección de memoria).
Es 0x40106C
En realidad lo que nos interesa es saber el desplazamiento en bytes que hay desde la dirección base del ejecutable hasta el punto en el que queremos parchear.
Se puede tomar en cuenta la sección de código también pero no es la base del ejecutable, recordar al momento de hacer la cuenta..
Bueno el offset (desplazamiento) es 0x6C y 0x1000 de la sección de código.
Bueno sabiendo estas cosas, ya vamos al código..
Con respecto al código, como decía antes, la idea viene de algo que estaba mirando sobre llamadas seguras a funciones para evitar detecciones de un anticheat por ejemplo,,
Por eso fui a crear una función (Check()) que trata de ser en forma figurativa la detección de modificaciones en la memoria.
Aunque en realidad una función de este tipo no tiene mucho sentido en el mismo hilo de ejecución, se debería hacer desde otro..
Y otro concepto que se puede apreciar en todo esto, es la importancia de restaurar la memoria luego de modificarla y lograr lo deseado.
Por eso , en este programa luego de realizar el parche y de llamar a la función de prueba (Target()) , se procede a restaurar la memoria a como estaba antes, para que las detecciones no se disparen.
Véase el bloque condicional donde se desea poder ingresar
Código:
...
if(master==MASTERNUM)//Aquí es donde queremos entrar XD
{ ...
Código
// // By 85 // Credits: Organner // elhacker.net // etalking.com.ar // 2013 // /////////////////// #include<windows.h> #include<stdio.h> ////////////////////// DWORD dwPatchPlace = 0x00000000; BYTE Opcode_JZ = 0x74; BYTE Opcode_JNZ = 0x75; /////////// inline void Patch(){//Función de parcheadora if(*(PBYTE)dwPatchPlace!=Opcode_JZ) return; DWORD dwOldProtect; VirtualProtect( (LPVOID)dwPatchPlace, 1, PAGE_EXECUTE_WRITECOPY, &dwOldProtect ); WriteProcessMemory( GetCurrentProcess(), (LPVOID)dwPatchPlace, &Opcode_JNZ, 1, NULL ); VirtualProtect( (LPVOID)dwPatchPlace, 1, dwOldProtect, &dwOldProtect ); } // inline void UnPatch(){//Función restauradora if(*(PBYTE)dwPatchPlace!=Opcode_JNZ) return; DWORD dwOldProtect; VirtualProtect( (LPVOID)dwPatchPlace, 1, PAGE_EXECUTE_WRITECOPY, &dwOldProtect ); WriteProcessMemory( GetCurrentProcess(), (LPVOID)dwPatchPlace, &Opcode_JZ, 1, NULL ); VirtualProtect( (LPVOID)dwPatchPlace, 1, dwOldProtect, &dwOldProtect ); } //////////////////// void Target(){//Función objetivo para ser alterada while(1){ #define MASTERNUM 85 int master=0x99999997; char* ingreso = new char[256]; system("cls"); printf("Ingrese la llave maestra\n"); scanf("%s", ingreso); if(!strcmpi(ingreso, new char[]= "key85\0")){ master = 85; } delete []ingreso; if(master==MASTERNUM)//Aquí es donde queremos entrar XD { printf("FELICITACIONES! USTED HA INGRESADO\n"); printf("\n"); system("pause"); break; } // if(GetAsyncKeyState(VK_END)) break;// En otro hilo if(!strcmpi(ingreso, new char[]= "exit\0")) break; } } // void Check()//Función que representa un método de seguridad { if(*(PBYTE)dwPatchPlace != Opcode_JZ) { printf("0x%X\n",*(PBYTE)dwPatchPlace); printf("Memoria alterada!, se sale del programa..\n"); printf("\n"); system("pause"); ExitProcess(45); } } ///////// int main(){ // We have to replace JZ with JNZ. dwPatchPlace=(DWORD)GetModuleHandle(NULL);//Retorna la BaseAddress //Sumamos el offset obtenido del desensamblado dwPatchPlace+=0x00001000; dwPatchPlace+=0x6C; //Logs printf("0x%X\n",(DWORD)GetModuleHandle(NULL)); printf("0x%X\n",dwPatchPlace); printf("0x%X\n",*(PBYTE)dwPatchPlace); printf("\n"); system("pause"); //Parcheamos la memoria antes de llamar a la función Patch(); //Llamamos a la función objetivo Target(); //Volvemos a dejar la memoria como estaba antes // UnPatch(); //Se deja que las comprobaciones de seguridad sigan su curso normalmente Check(); return 0; }
Unas imágenes, aunque voy a dejar el proyecto para descargar.
La llamada a UnPatch() está comentada, por eso la detección se dispara..
Un comentario acerca del uso de WriteProcessMemory, no es necesario,,
ya que con simplemente hacer esto:
Código:
*(PBYTE)dwPatchPlace= Opcode_JNZ;
lo que pasa que ambas funciones fueron sacadas de un post de un tal Organner, que algunos deben saber quien es, sólo les cambié una cosa y listo, aunque tampoco son algo especial como para que deban tener un autor XD
Proyecto MSVC6
http://www.mediafire.com/?j7s6a85bp4k5p3t
Este tutorial está orientado a gente principiante en la programación, no sea considerado un tutorial avanzado
Hasta Luego