lunes, 25 de marzo de 2024
Deserializacion de Clases
Deserialización de clases
En esta entrada vamos a terminar lo empezado en esta otra: Serialización de clases
Lo primero: Aquí voy a completar la clase creada en la otra entrada. Doy por supuesto que se sabe lo explicado en la entrada anterior.
Recordemos el código final de la entrada anterior:
class Prueba{
public:
int n;
string s;
string serializar(){
string t;
// int n
t.append((char*)&n, sizeof(n));
// string s
unsigned int size = s.size();
t.append((char*)&size, sizeof(size));
t.append(s.c_str(), size);
return t;
}
};
La idea ahora es deserializarlo, es decir, recuperar los datos guardados (serializados) en una string (la generada por el método "serializar").
Lo primero, veamos un ejemplo de main que utilizaremos para probar el correcto funcionamiento de ambas funciones:
int main() {
Prueba prueba;
prueba.n = 66;
prueba.s = "Code0x66";
String objetoSerializado = prueba.serializar();
prueba.n = 0;
prueba.s = "";
prueba.deserializar(objetoSerializado);
cout << prueba.n << '\n' << prueba.s << endl;
}
En orden:
Guardamos datos en el objeto "prueba"
Serializamos el objeto y guardamos el resultado en la variable "objetoSerializado"
Cambiamos los valores del objeto para asegurarnos de que la deserialización es correcta
Deserializamos a partir de la string almacenada en la variable, y mostramos los valores para corroborar su funcionamiento
Ahora hablemos del método "deserializar". Qué parámetros tendrá, qué retornará, cómo será...
Lo primero es que una deserialización puede ser correcta o no. En caso de que no lo sea, podemos informar lanzando una excepción, o, como haremos ahora, retornando un bool, que será "false" en caso de que hubiera error. (Un error se genera si la string a deserializar no tiene un formato correcto)
Una de las cosas más tediosas de la deserialización es aplacar errores y verificar la string. Esto lo haremos de último, para ver los problemas que puede dar.
El método recibirá como parámetro la string a deserializar, y retornará un bool indicando si ha deserializado correctamente.
Empecemos. Observando el método "serializar", podemos ver que genera una cadena con este formato (en binario):
[n (int)][s.size() (unsigned int)][s (char...)]
Lo primero, el orden. En el mismo orden que serializamos, deserializamos. Así que empezaremos por la variable "n", que es int. En el comienzo del método, guardaremos en un char* la cadena, ya que será más cómodo trabajar así con ella.
bool deserializar(const string& str){
const char* cadena = str.c_str();
int indice = 0; // Indice de la cadena por el que vamos
// Haciendo un cast de char* a int*, interpretamos la memoria como int
// Con lo cual, al desreferenciarla (*), obtenemos el int tal y como lo guardamos
int nuevaN = * (int*) cadena;
// Incrementamos el indice de la cadena
indice += sizeof(int);
// Aquí ya sumamos el indice a la cadena, para saltar lo ya leido
// Podría haberse hecho la primera vez también
// Pero es superfluo y lo dejo asi como ejemplo
unsigned int sizeS = * (unsigned int*) (cadena + indice);
indice += sizeof(unsigned int);
// Aqui toca leer caracteres. Pero aprovecharemos que tenemos la string
// con su metodo substr(indiceInicial, cantidad)
string nuevaS = str.substr(indice, sizeS);
// Por ultimo, asignamos las variables
s = nuevaS;
n = nuevaN;
}
Ya está el código explicado. Comentemos algunos detalles, empezando por lo último. La razón de que se asignen los campos del objeto al final es simple: Asegurar la integridad del objeto. O se deserializa todo, o no se deserializa nada. En caso de error, el objeto estará exactamente igual que antes.
Y hablando de errores, veréis que no he puesto ningún return, y es una función bool.
¿Qué clase de errores se pueden generar aquí? Por orden de aparición:
El primer int son sizeof(int) bytes (generalmente, 4 char). Habría error si la cadena a deserializar tuviera menos de 4 caracteres (estaríamos leyendo memoria fuera de la cadena).
Lo mismo con el tamaño del campo "s", solo que acumulamos tamaño. 4 anteriores más 4 bytes del unsigned int hacen 8 caracteres. Si la string tiene menos de 8 caracteres, habrá un error. Esto lo podemos comprobar ya al principio, y obviamos la primera comprobación. 8 es el tamaño mínimo de la cadena. Ahora, veamos el tamaño justo.
Cuando leamos el tamaño de la cadena, sabremos el tamaño total exacto de la string a deserializar, que será 4 + 4 + tamañoCadena. Lo único a remarcar, y no menos importante, es que esta comprobación a de ir exactamente en el momento en que tengamos los datos necesarios (el tamaño de la cadena).
Bien pues, veamos como quedaría si retornamos "false" cuando la cadena no tiene el tamaño mínimo (sin los comentarios de antes):
bool deserializar(const string& str){
const char* cadena = str.c_str();
int indice = 0;
// Primera condición
if(str.size() < sizeof(int) + sizeof(unsigned int)){
return false;
}
int nuevaN = * (int*) cadena;
indice += sizeof(int);
unsigned int sizeS = * (unsigned int*) (cadena + indice);
indice += sizeof(unsigned int);
// Segunda condición
if(str.size() < sizeof(int) + sizeof(unsigned int) + sizeS){
return false;
}
string nuevaS = str.substr(indice, sizeS);
s = nuevaS;
n = nuevaN;
// No olvidar el retorno "true" por defecto
return true;
}
Con esto, queda casi listo. Queda hacer el código para probarlo. He aquí el main:
int main() {
Prueba prueba;
prueba.n = 66;
prueba.s = "Code0x66";
string objetoSerializado = prueba.serializar();
prueba.n = 0;
prueba.s = "";
if(prueba.deserializar(objetoSerializado)){
cout << prueba.n << '\n' << prueba.s << endl;
}else{
cout << "Error deserializando" << endl;
}
}
Podéis probar el retorno false dándole una cadena incorrecta, por ejemplo: prueba.deserializar("test");
Un detalle más. También podéis retornar false en caso de que la cadena no tenga exactamente el tamaño que debe tener, es decir, que es más grande de lo que vosotros vais a consumir. Eso podría hacerse cambiando el operador "<" del último if por el operador "!=". Si es diferente al tamaño que pedimos, error: if(str.size() != sizeof(int) + sizeof(unsigned int) + sizeS)
Para probarlo, basta hacer: prueba.deserializar(objetoSerializado + "datos extra")
Y con esto, queda lista la deserialización. Es un proceso más lento y tedioso que la serialización en cuanto a tiempo programando, principalmente por todas las comprobaciones que haremos, pero es un proceso necesario.
Por último, y para despedir esta entrada, hay otra forma de hacer un método de deserialización. Deserializar no necesita que exista un objeto; deserializar va a generar un objeto a partir de una cadena (en este ejemplo, realmente estamos generando otro objeto y poniéndolo encima del que ya teníamos). Así pues, otra forma de hacerlo, sería con un método static. La forma de llamarlo podría ser esta, tirando excepción si falla (no lo voy a definir, eso es cosa del que lo quiera hacer):
Prueba prueba = Prueba::deserializar(objetoSerializado);
Y ahora sí, cierro entrada. Hace tiempo que tenía esta entrada pendiente; fue gracias a un comentario que lo recordé ;)
Un saludo, hasta próximas entradas!
Introduccion a ASM
Introducción a Ensamblador
/*Este texto no es de mi autoria, es del blog code 0x66.*/
Hola de nuevo. Hace ya dos semanas que no subo ninguna entrada, así que aquí va. Vamos a ver una breve introducción a Ensamblador, para quién lo quiera programar.
No es el lenguaje que recomiendo para empezar a programar, pero es un lenguaje muy importante.
Con el Cheat-Engine, con el botón "Memory View", podéis ver todas las instrucciones del proceso elegido. Las instrucciones de los programas, están en hexadecimal, que se traduce a Ensamblador. Con el Cheat-Engine podéis echarle un vistazo. Ahora nos centraremos en programar Ensamblador, y hacer programas con él.
Para empezar, un compilador. Yo uso Flat Assembler. Si lo queréis, lo tenéis en su web: flatassembler.net.
Ensamblador es un lenguaje sencillo, en el sentido de que hay unas 256 instrucciones diferentes (de las cuales la mayoría son parecidas entre sí). Además, también habrá que llamar a funciones de librerías, como la WinApi, por ejemplo, o hacer vuestras propias funciones. Por otra parte, ASM es un lenguaje rápido, por el simple hecho de que las instrucciones que escribáis, serán aproximadamente las mismas que haya en memoria. Es decir: el código que escribáis, será el código del proceso.
Una vez descargado el FASM (u otro), lo abrimos, y empezamos un nuevo programa. Para los nuevos en ASM, usaré esta entrada para explicar algunas isntrucciones en ASM, y otras cosas básicas.
Empecemos:
En ASM, no hay variables, como en otros lenguajes. Hay registros y la pila o stack. Para tratar con datos, usaremos los registros. Para almacenar datos, usaremos la pila. (Generalmente: En la pila se suele guardar punteros a datos además de datos en sí)
Los registros básicos son: AX, BX, CX y DX. Estos registros, además, se pueden descomponer en 2 partes: HIGH y LOW (AH y AL o BH y BL).
Veamos más claramente cómo son los registros (en hexadecimal):
(8 bits = 1 byte = '00')
RAX: 64bits 00 00 00 00 00 00 00 00
EAX: 32 bits 00 00 00 00
AX: 16 bits 00 00
AH: 8 bits 00 . .
AL: 8 bits . . 00
Según el hardware, se suelen usar actualmente RAX o EAX. Esto no excluye de usar los demás.
La pila es donde se guarda información, llamadas a funciones, etc. Es una zona de memoria, donde "Lo último en escribirse, es lo primero en leerse", o lo que es lo mismo, "Lo último en entrar, es lo primero en salir". Simplemente, para que os hagáis una idea, es como una pila de libros. El último libro que apiles, será el primero en cogerse.
Igulamente, también podréis acceder al "centro" de la pila. Pero eso ya se verá más adelante.
Para acceder a la pila, necesitamos un puntero. Un puntero que apunte a ella. Para ello, tenemos otros 2 registros: EBP y ESP. Cada uno de ellos apunta a la pila.Ya veremos más adelante las diferencias entre los dos.
Además, hay otro pequeño pero muy importante "registro": las flags o banderas. Estas tienen 2 valores: 1 y 0. Cada una de ellas, se activa o desactiva (1 o 0) dependiendo del resultado de algunas operaciones. Por ejemplo, la flag zero, se activa si alguna operación da 0.
Ahora que vimos lo básico, empezaremos con algunas instrucciones:
MOV A,B: Esta instrucción, copia B sobre A. A será igual a B. Se puede poner: MOV EAX, 8. Así, el registro EAX será igual a 8. Suponiendo que EAX fuera un puntero a una zona de memoria, y quisieramos copiar 8 a esa dirección, haríamos: MOV [EAX],8. Los corchetes indican que tratamos ese valor como un puntero. También podríamos poner: MOV EAX,[EBX]. Esto copiaría el valor del puntero EBX a EAX. Múltiples posibilidades.
INC A: Incrementa A en 1. Si EAX fuera 10, y ejecutamos INC EAX, EAX pasaría a ser 11.
DEC A: Decrementa A. Igual que INC, pero decrementando.
ADD A,B: Añade B a A. A será igual a A+B.
DEC A,B: Resta B a A. A será igual a A-B.
AND A,B: Operación AND a nivel de bit. El resultado se guarda en A.
OR A,B: Operación OR.
XOR A,B: Operación XOR u OR-exclusiva.
CMP A.B: Esta es muy impotante. Resta B a A, pero no guarda el resultado. Simplemente, se activan las flags correspondientes. Se usa en conjunto con saltos condicionales.
TEST A,B: Como CMP, pero en evz de una resta, hace una operación AND a nivel de bit.
JMP A: Mueve el registro IP (guarda la dirección de la instrucción actual) a la dirección A. Es decir, salta a otra parte del código.
Saltos condicionales: Actuan como JMP, pero saltan o no según las flags activas. Por ejemplo, el salto JZ, "jump if zero" salta solo si está activa la flag zero. JNZ, sería lo contrario. Salta si la flag zero NO está activa. Generalmente se usa CMP antes de un salto condicional.
CALL A: Hace un salto como JMP, pero, además, guarda la dirección de la instrucción en la pila, entre otras. Se usa para llamar a subprogramas, a "funciones".
RET A: Pone el registro de instrucción en la instrucción marcada por el registro de pila EBP más A. Se puede no poner A, y dejarlo como "RET". En resumen, esto retorna a la dirección desde la que se llamó a la función con un CALL.
;comentario: Esto no es una instrucción, y no aparecerá en el ejecutable compilado, pero cuando se coloca ';', lo que le sigue es un comentario.
Además, hay instrucciones para trabajar con decimales (float). Pero eso ya no entra aquí. Para que podáis compilar, y hacer pruebas, os dejo un pequeño código, para que vayáis descifrando. Además, FASM trae en su carpeta, la carpeta "EXAMPLES", donde veréis códigos variados, todos ellos en ASM.
format PE GUI 4.0
section '.data' data readable writeable
var db 'Cadena' ; db : Pone en memoria la cadena
; para acceder a ella, se usa el identificador,
; en este caso "var"
include 'win32ax.inc' ; WinApi. Include sirve para usar funciones de librerías
.code ;Empieza el código
start: ; Esto es una etiqueta. Con esto se puede hacer JMP .
call icf_init ;llamada a una función. (Más abajo está la etiqueta)
mov eax,var ;EAX tendrá la dirección de la cadena (var)
call write ;otra etiqueta
invoke MessageBoxA,HWND_DESKTOP,var,"Titulo",0 ; Una ventana (MessageBox)
invoke Sleep,-1 ;WinApi. Deja el programa en pausa
.end start
icf_init:
invoke AllocConsole
ret
malloc: ; EAX: Size
invoke VirtualAlloc,NULL,eax,MEM_COMMIT+MEM_RESERVE,PAGE_READWRITE
ret
free: ; EAX: Address
invoke VirtualFree,eax,edx,MEM_RELEASE
ret
write: ; EAX: String
push ECX ;Uso esta función para escribir en consola
push EDX
mov edx,eax
xor ecx,ecx ;en ECX se guardará el tamaño de la string
dec edx ;para compensar el inc que sobra en el loop
write_loop_1:
inc edx
inc ecx
cmp byte[edx],0 ;hasta que el char de la string sea 0
jnz write_loop_1
dec ecx ;se elimina el contador sobrante.
invoke WriteConsole,,eax,ecx,1,0
pop EDX
pop ECX
ret
Suerte, y hasta una próxima entrada!
PD: Las funciones las hice yo, y quizás tengan fallos. Tomadlas solo como ejemplos. Si algo no os funciona, podéis dejar un comentario, y veré que puede pasar.
Algoritmos Recursivos vs Iterativos
Algoritmos
Recursivos vs Iterativos
En esta entrada, veremos, en lenguaje de programación C++, diferencias y ejemplos entre estos dos tipos de algoritmos: Los algoritmos recursivos, y los algoritmos iterativos.
Veremos esto tomando como ejemplo la Sucesión de Fibonacci (0,1,1,2,3,5,8...).
Para hayar un término n de la sucesión, tenemos esta función:
F(n) = 0 si n= 0.
F(n) = 1 si n= 1. y para cada numero se cunple que: F(n-1)+F(n-2) si n > 1
En resumen: cada término, es la suma de los dos anteriores. Salvo el término 0 y el 1, que son 0 y 1, respectivamente.
Una función recursiva, es la que se llama a si misma. Para que una función recursiva tenga fin, ha de tener una condición, como en este ejemplo: "si n=0" o "si n=1".
Veamos la función Fibonacci recursiva en C++:
uint64_t fib_recursivo(uint64_t n){
if(n==0) return 0;
if(n==1) return 1;
return fib_recursivo(n-1) + fib_recursivo(n-2);
}
PD: uint64_t es lo mismo que unsigned long long int.
En esta sencilla función, vemos claramente la definición de la serie de Fibonacci. N es el término de la serie que queremos conseguir.
Ahora veamos la forma iterativa:
uint64_t fib_iterativo(uint64_t n){
if(n==0) return 0;
if(n==1) return 1;
uint64_t a=0, b=1, c=0;;
for(int i=2; i<=n; i++){
c = a + b;
a = b;
b = c;
}
return c;
}
En la iterativa, vamos generando los términos en orden hasta llegar al que buscamos. en la variable 'a', guardamos lo que sería f(n-2), y en 'b', f(n-1). Luego, con el ciclo, igualamos 'c' a 'a' + 'b' ( f(n-2) + f(n-1) ).
Entre los algoritmos iterativos y los recursivos, suele haber estas diferencias básicas:
La forma recursiva, es mucho más lenta que la forma iterativa, especialmente para números "grandes" (Ej. Para N=100, fib_recursivo() tardará mucho en terminar)
La forma recursiva suele ser más sencilla de realizar que la iterativa. Aunque esto depende de la sucesión/algoritmo que busquemos.
A parte, os muestro otra función, parecida a la iterativa, pero con algunos cambios:
uint64_t fib_mezcla(uint64_t n){
static vector v;
if(!v.size()){
v.push_back(0);
v.push_back(1);
}
if(v.size()<=n)
for(uint64_t i=v.size(); i<=n; i++)
v.push_back(v[i-1] + v[i-2]);
return v[n];
}
Esta, lo que hace es guardar los valores que obtiene (por el método iterativo) en un vector static.
Al ser una variable static, no será borrada al acabar la función, lo que significa que guardará los valores que tiene cada vez q llamemos a la función. De esta manera, ahorramos tiempo, ya que no tenemos que calcular (salvo la primera vez), cada término de fibbonaci. A cambio, tiene un mayor gasto de memoria. Pero en este caso, dado que apenas guardará más de 87 términos (a partir de ahí se sale del tamaño de una variable de 64 bits), apenas notaremos su gasto de memoria.
Aquí he hecho unas pruebas comparativas de las 3 funciones:
En la primera prueba, se mide el tiempo que tarda en llamar 1 vez a la función para el término 40. Como podemos observar, el método recursivo tarda más de 4 segundos, mientras que los demás, apenas tardan unos milisegundos.
En la segunda prueba, omito la función recursiva, ya que tardaría un tiempo o años en acabar. Aquí podemos ver lo que tardan los otros métodos al ser llamados 1.000.000 veces, para el término 85. Aquí es donde podremos apreciar la diferencia entre el método iterativo tradicional, y el método donde se guardan los valores. El método iterativo, se llama 1.000.000 veces, y las 999.999 veces hace todo el ciclo desde el 1 hasta el 85. En cambio, el otro método hace solo 1 vez el ciclo, y las otras 999.999 veces, simplemente retorna el valor guardado en el vector.
Y hasta aquí este resumen sobre los pros y contras de las funciones recursivas e iterativas. En vuestros programas, os recomiendo poner un límite para las funciones recursivas que podrían tardar mucho, como la de fibonacci, para así evitar que el programa se detenga.
Manejo de Archivos
Manejo de archivos[Python]
/*Este texto no es de mi autoría, es de Overload's blog.*/
Aquí va el primer miniartículo de la revista, como dice en el título manejo de archivos mediante la función open.
Bueno, la función open toma como parámetros el archivo a abrir y el modo, es decir, lectura, escritura, etc.
Sería así:
#!usr/bin/env python
open('archivo.txt', 'r')
Con este código se abriría al archivo 'archivo.txt' en modo de lectura.
Los parámetros son estos:
'r' - Modo lectura ('r'ead), si no existe el archivo nos salta una excepción IOError
'w' - Modo escritura ('w'rite), se abre el archivo para poder escribirlo, si no existe se crea. Además
sobreescribe las cosas en el archivo, es decir, las borra y escribe las nuevas.
'b' - Abre el archivo en modo binario ('b'inary)
'a' - Modo añadir('a'ppend), abre el archivo en modo w, es decir, modo escritura. Pero este en cambio no
sobreescribe sino que escribe las cosas al final del archivo.
Bien ahora veamos los métodos que tiene open para manejar archivos:
Métodos relacionados al parámetro 'r':
Método read:
Su nombre lo indica todo...(leer :-p)
abrir = open('archivo.txt', 'r') #abrimos el archivo en modo lectura
leer = abrir.read() #leemos el archivo
print leer #y mostramos por pantalla lo leído
Método readline:
Lee las líneas del archivo una por una:
abrir = open('archivo.txt', 'r') #abrimos el archivo en modo lectura
leer = abrir.readline() #leemos el archivo linea a linea
print leer #y mostramos por pantalla lo leído
Método readlines:
Lee las líneas pero devuelve una lista con cada una de ellas es decir:
Tenemos un archivo.txt:
hola
adiós
hasta luego
Y el código python:
abrir = open('archivo.txt', 'r') #abrimos el archivo en modo lectura
leer = abrir.readlines() #leemos el archivo
print leer #y mostramos por pantalla lo leído
Python nos mostrará:
['hola\n', 'adiós\n', 'hasta luego']
Métodos relacionados al parámetro 'w' y 'a':
Nota*: El método write pierde la capacidad de sobreescribir(borrar lo escrito y escribir algo nuevo) cuando especificamos el parámetro 'a'.
Método write:
Escribe en el archivo tomando como parámetro una cadena y la sobreescribe.(Como explique antes si ponemos de parámetro 'a' pierde la capacidad de sobreescribirse)
Ejemplo:
abrir = open('archivo.txt', 'w')
escribir = abrir.write('bon dia')
Método writelines:
Escribe el archivo tomando como parámetro una cadena pero no la sobreescribe sino que la pone al lado:
abrir = open('archivo.txt', 'w')
escri = abrir.writelines('bon dia')
¿Qué pasa si quiero añadir un salto de línea?, pues añadimos \n.
abrir = open('archivo.txt', 'a')
escri = abrir.writelines('bon dia'+'\n')
Métodos relacionados con el parámetro 'b':
Se utiliza normalmente para el manejo de datos externos a tu ordenador, datos ftp, http, tcp/ip, etc.
En fin, esto es todo!
Un saludo a todos!!!
Recoger datos de CMD
Minitutorial recoger datos de cmd [Python]
/*Este texto no es de mi autoria, pertenece al Overload's blog.*/
En algunos exploits veréis que os dice en las instrucciones:
Modo de uso: exploit.py
Y supongo que os habréis preguntado como hace python para recoger esos datos que tu introduces ahí.
Bien pues os lo explico que es muy facilito.
Existe una librería llamada sys para manejar con cosas del sistema de ahí viene su nombre(sys - system).
Una de esas cosas es recoger datos de la linea de comandos de cmd. ¿Cómo se hace esto?, pues con
el método argv...
Os lo explico paso a paso:
#!/usr/bin/env python
import sys #importamos la librería sys
host = sys.argv[1] #bien, aquí es donde se recogen los datos: el método argv #contiene un array con los datos que tu introduzcas, en este caso se asignará al #segundo dato del array el string que vaya después de exploit.py (el primer dato #sería exploit.py y el segundo lo que hayamos introducido sys.argv =
#['exploit.py','loquehayasintroducido'])
print host # nos imprimiria sys.argv[1] que es lo que hayamos introducido
Bueno esto es, no es más porque es muy fácil ^^
Un saludo!!
miércoles, 24 de enero de 2024
Ahorcado
/*Codigo posteado por satu en elhacker.net thread: fgets para enteros?*/
#include <stdio.h>
#include <string.h>
int main (){
char palabra [7]; char final [7];char car;
int cont;
memset (&final, '-', 6);final [6] = '\0';
printf ("Escribe una palabra (lenght <7 chars): ");
fgets (palabra, 7, stdin);palabra [6] = '\0';
while (strcmp (palabra, final) != 0){
while(getchar() != '\n')
printf ("\nEscribe una letra: ");
car = fgetc (stdin);
for (cont=0; cont<6; cont++)
if (car == palabra [cont])
if (car == palabra [cont])
final [cont] = car;printf ("%s\n", final);car = 0;}
printf ("Well done! ;)\n\n");
while(getchar() != '\n') getchar();
return 0;
}
sábado, 6 de enero de 2024
Cambiar Zona Horaria
Intro
Respaldar
/etc/localtime
/usr/share/zoneinfo Lo ideal seria usar systemdatectl o date pero en caso de que no puedas como en kali o simplemente no quieras se puede hacer manualmente
Respaldo Primero hay que ir como root a /etc/localtime/ y usar
/etc/localtime/ La zona horaria que usa nuestro linux se encuentra en /etc/localtime/ (en etc esta toda configuracion)
/usr/share/zoneinfo Nuestra Zona Horaria se encuentra en /usr/share/zoneinfo ,debemos ir a ella por consola como root y luego copiarla en vez de /etc/localtime (nuestra zona horaria), basta con usar:
luego de esto aplicar reboot
Nuestra hora puede ser colocada dentro del $PS1. Espero les haya servido ¡Hasta la proxima!.
Respaldar
/etc/localtime
/usr/share/zoneinfo Lo ideal seria usar systemdatectl o date pero en caso de que no puedas como en kali o simplemente no quieras se puede hacer manualmente
Respaldo Primero hay que ir como root a /etc/localtime/ y usar
cp localtime ~localtime
para crear un archivo ~localtime en caso de que falle./etc/localtime/ La zona horaria que usa nuestro linux se encuentra en /etc/localtime/ (en etc esta toda configuracion)
/usr/share/zoneinfo Nuestra Zona Horaria se encuentra en /usr/share/zoneinfo ,debemos ir a ella por consola como root y luego copiarla en vez de /etc/localtime (nuestra zona horaria), basta con usar:
cd /usr/share/zoneinfo ;cp zonahoraria localtime /etc/localtime
luego de esto aplicar reboot
sudo reboot
Nuestra hora puede ser colocada dentro del $PS1. Espero les haya servido ¡Hasta la proxima!.
Suscribirse a:
Entradas (Atom)