miércoles, 5 de junio de 2024
No encontrar las librerías .h en linux
viernes, 12 de abril de 2024
ELF Basics Internal: Elf Basics.
So let is begin with a very simple hello world program in C
int main(){printf("\nHello World\n");return 0;}
As I am on a 64 bit Linux system I will compile the binary for both 32bit and 64bit mode.
We will compile this code with gcc by issuing the commandfor 32 bit -> gcc hello.c -m32 -o hello32
, ( in case we get error we can install gcc multilib by issuing command sudo apt-get install gcc-multilib ) If we issue file command on the binary we created we would see the following output
pentest@ubuntu:~/Desktop$ file hello64 hello64: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=797fa6ea8a92b773eb5106c822a76788441ceac1, not stripped
pentest@ubuntu:~/Desktop$ file hello32 hello32: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=ba188ad09ee9ff9ac774833b8a7c87d8afbc443a, not stripped
So let us try to understand what all these mean (we will analyze the result of 64 bit binary)
hello64: This is the filename of the binary on which we are executing the file command
ELF - Executable and Linking Format or Executable and Linkable Format - This mean that the binary type is associated with mainly UNIX type operating system , like Linux, Solaris but also supports certain non UNIX operating system.
64bit - This gives us information tells about the architecture of the binary that it is 64 bit. So if it is a 32 bit binary it will be shown as 32 If we are in a 64bit machine and if we want to create a 32 bit binary we can pass the -m32 option
LSB - Least Significant Byte - It means the binary is in little endian format. In Intel architecture you will often find this as LSB. However in architectures like PowerPC , SPARC and so on it is possible to get this in big endian format i.e MSB ( Most Significant Byte )
Shared object - This result can either be Shared Object or Relocatable or Executable.
Let us see how these are different from each other and how we can generate them using gcc. The two terms which we are going to use here are PIC ( Position Independent Code ) and PIE ( Position Independent Executable) .
When we are planning to create a library that can be called by many process, we need to make it a PIC so that they can be loaded in the memory at any virtual address and just because they are position independent it can be accessed with relative offsets without worrying about the clashes of fixed locations in memory. We can create a PIE when Shared Object - By default the gcc compiler compiles the source code with -fPIC which makes address of the sections in the program relative to each other.
Executable - This mean this is not a PIE application. This loads with absolute address and thus we can find no reference of .plt.got sections here as the program is loaded in memory with fixed address .We can disable PIE with -no-pie option in gcc and thus we will get a executable object file.
Relocatable - This means this is just an object code without any linking of libraries or files that are necessary for the execution.
There are some steps involved when we make a program that can be executable ( Please Note: The term executable here means here is to make it run or execute and should not be confused with the above executable object type ).
To make an executable from source program the following process is involved.
Normally in gcc we do in one step like gcc hello.c -o hello.out
but however we can do in 2 steps like gcc -c hello.c ; this will create an object file called hello.o This is how the disassembly of main looks like in object code.
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 8d 3d 00 00 00 00 lea rdi,[rip+0x0] # b <main+0xb>
b: e8 00 00 00 00 call 10 <main+0x10>
10: 90 nop
11: 5d pop rbp
12: c3 ret
This program cannot run or do anything because the object code doesn't have the necessary linked objects or libraries required for execution. We can generate an executable binary from object code using the command
gcc hello.o -o hello-executable Now if we run objdump on the binary we can see lots of sections getting created with the location to the linkers.
This is how the disassembly of main looks like after linking
63a: 55 push rbp
63b: 48 89 e5 mov rbp,rsp
63e: 48 8d 3d 8f 00 00 00 lea rdi,[rip+0x8f] # 6d4
<_IO_stdin_used+0x4>
645: e8 c6 fe ff ff call 510
<puts@plt>
64a: 90 nop
64b: 5d pop rbp
64c: c3 ret
64d: 0f 1f 00 nop DWORD PTR [rax]
Dynamically linked, interpreter /lib/ld-linux.so.2, - It means that the binary uses some dynamically linked libraries. There is 2 possible values possible for this.Dynamically linked and Statically Linked.
Dynamically Linked - It means the linker actually uses a reference to load dynamically linked libraries in memory during execution of the program from the location /lib/ld-linux.so.2
We can verify it by running the ldd on the binary
pentest@ubuntu:~/Desktop$ ldd helloNOPIE linux-vdso.so.1 (0x00007ffffe990000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcb5e8c0000) /lib64/ld-linux-x86-64.so.2 (0x00007fcb5ecb1000)
lunes, 25 de marzo de 2024
conversion explicita (casting)
C++, conversión explícita o cast Cuando necesitamos convertir una variable perteneciente a un tipo de dato (cadena, numérico, fecha, etc.) a otro tipo diferente del suyo original, debemos decirle al programa explícitamente que tipo de conversión deseamos.
La conversión explícita en vez de realizarla el compilador automáticamente se indica de forma explícita, en C++ utilizamos la forma (nombre_de_tipo)expresión. No es exclusiva de C++ pues también se puede emplear en otros programas aunque cambie ligeramente la implementación.
Notación cast Si tenemos una función previamente definida que espera un tipo determinado, por ejemplo la función raíz cuadrada (sqrt) espera como argumento un tipo double, para evitar una salida inesperada en el caso de pasar un argumento de otro tipo, podemos escribir: sqrt ( ( double ) ( n + 2) ); De este modo forzamos para que el resultado de ( n + 2 ) sea siempre un tipo double y se lo pase a la función sqrt.
En C++ es posible expresar también una construcción cast de la forma siguiente: nombre_de_tipo(expresión) esta recibe el nombre de notación funcional, y no se puede utilizar con tipos que tengan un nombre simple.
Notación funcional Para convertir un valor a un tipo puntero utilizando la notación cast escribiríamos: int *p = ( int *)0x1F5; Pero utilizando la notación funcional escribiremo: typedef int *pint; int *p = pint(0x1F5);
Una variable de un determinado tipo no siempre puede ser convertida explícitamente a otro tipo. En este caso: struct { unsigned int a : 3; // bits 0 a 2 unsigned int b : 1; // bit 3 unsigned int c : 3; // bits 4 a 6 unsigned int d : 1; // bit 7 } atributo; La variable atributo tiene una longitud de ocho bits. Pero si intentamnos copiar la variable atributo a una variable atrib de tipo char y escribimos. char atrib = char atributo; // error Da un error, ya que C++ no permite convertir una estructura a un tipo como char aunque las longitudes de ambos sean iguales.
Conversión de Punteros Utilizando conversiones explícitas de tipo sobre punteros, es posible convertir el valor de una variable de un determinado tipo a otro cualquiera. El formato general para hacer esto es: cualquier_tipo *p = ( cualquier_tipo *)&variable. Aplicando esto al caso anterior, obtendríamos: char * atrib = ( char *)&atributo; Con lo que char a = * atrib; define la variable a de tipo char, cuyo contenido es el mismo que el de la estructura atributo. Constructores y operadores de conversión Cuando trabajamos con clases, nosotros mismos tenemos que construir las conversiones que deseamos que realice el compilador cuando utilice un objeto de una clase. Estas conversiones pueden ser o entre una clase y un tipo predefinido. Para ello podemos utilizar dos mecanismos; constructores y operadores de conversión. Constructores Podemos definir una conversión a través de un constructor que tome un argumento de un determinado tipo como entrada y lo convierta en un objeto de una determinada clase. En la definición de una clase podemos definir el siguiente constructor: nombre_de_clase (int r) { real = ( double )r; imag = 0; } Que sirve para construir un número complejo. Este constructor además de inicializar un objeto complejo utilizando solamente un valor también permite asignar directamente un entero int a un objeto complejo como se muestra a continuación. complejo c(3); // construye el complejo (3,0) c = 6; // equivale a c = complejo(6) Operador de conversión Ahora se desea que se realice también de una forma implícita o explícita, si existe ambigüedad, la conversión de un tipo definido por el usuario a un tipo básico: double d; CRacional r(1,2); d = r ; // r tiene que convertirse a double para que la instrucción d = r se ejecute correctamente, es necesario realizar una conversión implícita de CRacional a double. Este tipo de conversión no está permitido con un constructor, ya que no podemos definir un constructor de un tipo base. Cuando necesitamos convertir objetos de un tipo de clase a otro tipo, tenemos que utilizar un operador de conversión. La sintaxis para este operador es: C::operator T(); Donde T es el nombre de un tipo. La conversión que se realiza es de C a T. Para convertir de CRacional a double. class CRacional { //....... operador double(); }; inline CRacional::operator double() { return ( double )numerador/( double )denominador; } Un operador de conversión no puede tener argumentos ni tipo del valor que se retorna. Un operador de conversión se puede llamar de a través de las formas siguientes: double d; CRacional r(1,2); d = r.operator double();/llamada explícita a la función d = double(r);//conversión explícita (notación funcional ) d = ( double )r; //conversión explícita cast d = r ; // conversión implícita Un operador de conversión puede llamarse explícitamente pero su principal utilidad es que sea llamado automáticamente por el compilador cuando la evaluación de una expresión requiere el tipo de conversión realizado por él. También puede definirse un operador de conversión que convierta un objeto de una clase a otro objeto de otra clase. Conversión del tipo void* El tipo void se puede utilizar para definir un puntero a un elemento genérico. Como podemos ver la función C malloc se define como: void *malloc(size_t n); En C, la conversión del tipo void* a otro tipo podía realizarse de forma implícita de este modo. char *p = malloc(longitud + 1); Pero en C++ esta conversión tiene que realizarse de forma explícita. Esto es. char *p = ( char * ) malloc ( longitud + 1 );









