Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - desmon_hak

#1
No tienes permitido ver enlaces. Registrate o Entra a tu cuentaEl MBR son los primero 512 KB del disco duro (recordarme la dimension si estoy confundido), lo mas sencillo es hacer lo que se hacia antes cuando instalar linux y windows era problematico. Haces un:
dd if=/dev/sda of=copiaMBR bs=512 count=1
y asi tienes una copia del MBR por si las moscas. ^^
Para restaurarla poner el if y el of en sentido inverso y listos.

El petya y esos ¿Solo joden el MBR?

Perdona amigo, no son 512kb, son 512bytes, el tamaño de un sector en el dico(comúnmente)
#2
No tienes permitido ver enlaces. Registrate o Entra a tu cuenta

Hola buenas, soy Desmon y soy un usuario nuevo en este foro, no se si incumpla alguna de las reglas del foro en este post pero si así que alguien me lo reporte para corregirlo cuanto antes.

Bueno, viniendo a lo que vengo. Estaba o mejor dicho, quería diseñar un allocator de memoria con GC(garbage collection), cual es la salsa del asunto, bueno, quiero hacerlo lo mas eficiente posible, multiplataforma, funcional, con protecciones de memoria y de acceso y quería hacerlo usando C puro, usando las libC lo menos posible y sus cabeceras(esto implica implementar las funciones necesarias para obtener largos de cadenas, imprimir texto, copiar bloques de memoria y etc).

Hoy lo que vengo a preguntar mas concretamente es si conocen algoritmos eficientes para la tarea de diseñar tal pieza de software, o si me recomiendan alguno en especifico.

Necesito uno o varios algoritmos para poder reorganizar la memoria si hay bloques liberados, o que hacer cuando hay memoria liberada de por medio y hay que reorganizar todo para poder hacer buen uso de ella.

Algoritmos para la recolección de basura, pues si, he visto algoritmos y tal, pero no el como se podría crear una implementación como tal por que me falta documentación, información o etc. Si alguien tiene DOC de haber realizado un proyecto similar, o que tiene algún consejo o etc, cualquier cosa es bien venida.

Por ahora he visto varias funciones para reservar memoria, una de ellas es "NtAllocateVirtualMemory" en el caso de Windows, esta la podemos situar en ntdll.dll, lo que estoy haciendo es cargarla en run time si se encuentra en Windows.

Yo estoy haciendo uso de una subrutina constructora que se llama antes que el main sin que el programador convencional lo sepa ni tenga que ser consciente de ello:
Código: c
void __init1__()
{

#ifndef __DEBUG__
    LPSTR errorText = NULL;
    DWORD error = NULL;
#endif

#if defined(WIN32) || defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__NT__)
    // cargar la ntdll
    ntdll = LoadLibraryA("ntdll.dll");
    if (ntdll == NULL)
    {
#ifdef __DEBUG__
        GET_LAST_ERROR_WIN()
        printf("Error: %s", errorText);
        printf("Error %p: No se pudo obtener el handle de la libreria ntdll.dll.\n", error);
        printf("Error al cargar ntdll.dll\n");
#endif
        ROW_ERROR_RET(ERROR_LOAD_LIB)
    }

    // cargar la funcion NtAllocateVirtualMemory de ntdll
    NtAllocateVirtualMemory = (NtAllocateVirtualMemory_t)GetProcAddress(ntdll, "NtAllocateVirtualMemory");
    if (NtAllocateVirtualMemory == NULL)
    {
#ifdef __DEBUG__
        GET_LAST_ERROR_WIN()
        printf("Error: %s", errorText);
        printf("Error %p: No se pudo obtener NtAllocateVirtualMemory de ntdll.dll.\n", error);

        printf("Error al obtener puntero a NtAllocateVirtualMemory\n");
#endif
        TROW_ERROR_RET(ERROR_LOAD_FUNC)
    }
#endif
    NtFreeVirtualMemory = (NtFreeVirtualMemory_t)GetProcAddress(ntdll, "NtFreeVirtualMemory");
    if (NtFreeVirtualMemory == NULL)
    {
#ifdef __DEBUG__
        GET_LAST_ERROR_WIN()
        printf("Error: %s", errorText);
        printf("Error %p: No se pudo obtener NtFreeVirtualMemory de ntdll.dll.\n", error);

        printf("Error al obtener puntero a NtFreeVirtualMemory\n");
#endif
        TROW_ERROR_RET(ERROR_LOAD_FUNC)
    }

#ifdef __DEBUG__
    debug_print("__init1__ __attribute__((constructor))")
#endif
        RegistroTablas.n_tablas = (_uint64_t){0};
    RegistroTablas.tablas = NULL;
    /*
     *  Usar create_table_blocks() para reservar la primera memoria del programa
     *  y que el pogramador no necesite crear una por su cuenta
     *  sino que pueda reservar memoria en ella directamente
     */
    return;
}

primero voy a decir que la macro __DEBUG__ solo la defino a la hora de compilar con -D__DEBUG__ para que pueda ir viendo la información de todo mientras se corre la aplicación que hace uso de esta. La defunción de ntdll esta definido en mi header MemoryAddressBase.h como "HMODULE ntdll;", y el pedazo de codigo que acabáis de ver se encuentra en MemoryAddressBase.c.
La declaración de la función __init1__ se encuentra en el header principal como:
Código: text
void __attribute__((constructor)) __attribute__((visibility("hidden"))) __init1__();
void __attribute__((destructor)) __attribute__((visibility("hidden"))) __end1__();

al igual la rutina destructora. Volviendo a nuestra subrutina constructora, usamos LoadLibraryA, una función de la WinApi que carga ntdll.dll en tiempo de ejecución, como es LoadLibraryA se esta usando la versión ANSI de la función. Si al cargarla da algun error ntdll queda con valor nulo y se ejecuta un ROW_ERROR_RET se este definiendo __DEBUG__ o no. TROW_ERROR_RET se define en el header "ErrorAllocator.h" de la siguiente manera:
Código: c
#define TROW_ERROR_RET(ERROR)                   \
    printf("Error(ErrorAllocator): %p", ERROR); \
    return;
una simple definición que hace uso de un printf para mostrar el error y un return;. El error ERROR_LOAD_LIB se define en la misma header mediante un enum que podemos ver:
Código: text
typedef enum ErrorAllocator
{
    SUCESS,
    ERROR_LOAD_LIB,
    ERROR_LOAD_FUNC
} ErrorAllocator;
Adicionalmente decir que tenemos una función "printErrorAllocator" que recibe el codigo de error y imprime un mensaje acorde a este:
Código: c
void printErrorAllocator(ErrorAllocator err)
{

    switch (err)
    {
    case SUCESS:
        puts(MSG_ERROR_SUCESS);
        return;
    case ERROR_LOAD_LIB:
        puts(MSG_ERROR_LOAD_LIB);
        return;
    case ERROR_LOAD_FUNC:
        puts(MSG_ERROR_LOAD_FUNC);
        return;
    default:
        puts(MSG_ERROR_UNKNOW_ERROR);
        return;
    }
}

Volviendo a la función constructora, en el caso de que se use la macro __DEBUG__ usamos GET_LAST_ERROR_WIN que se define en MemoryAddressBase.h de la siguiente manera:
Código: c
#ifdef __DEBUG__
    LPSTR errorText = NULL;
    DWORD error = NULL;
#endif
#define GET_LAST_ERROR_WIN() error = GetLastError(); \
    FormatMessage( \
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, \
        NULL,\
        error, \
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\
        (LPSTR)&errorText, \
        0,\
        NULL \
    );

Dicha me permite obtener información mas detallada sobre el error que se produjo al cargar la librería. Prosiguiendo con la subrutina, se obtiene la dirección "NtAllocateVirtualMemory" usando la función GetProcAddress, NtAllocateVirtualMemory y NtAllocateVirtualMemory_t se definen en el header principal de la siguiente manera:
Código: c
typedef NTSTATUS(__stdcall *NtAllocateVirtualMemory_t)(
    HANDLE ProcessHandle,
    PVOID *BaseAddress,
    ULONG_PTR ZeroBits,
    PSIZE_T RegionSize,
    ULONG AllocationType,
    ULONG Protect);
NtAllocateVirtualMemory_t NtAllocateVirtualMemory;

y de igual manera hacemos con "NtFreeVirtualMemory", aquí sus declaraciones:
Código: c
typedef NTSTATUS(__stdcall *NtFreeVirtualMemory_t)(
    HANDLE ProcessHandle,
    PVOID *BaseAddress,
    PSIZE_T RegionSize,
    ULONG FreeType);
NtFreeVirtualMemory_t NtFreeVirtualMemory;

Esto es en el caso de windows, pero en el caso de linux en un inicio he pensado usar mmap, una syscall que permite al kernel pedir memoria para tratarla como queramos, se podria decir que es la función NtAllocateVirtualMemory para linux.
Se que dicha funcion se puede usar con sus cabeceras, pero no lo quise, en su lugar opte mejor usar Assembly si era posible, entonces primero veamos la declaración de mmap en nuestro header principal como:
Código: c
void *mmap_syscall(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

y su respectiva declaración para Linux en arquitectura de 32 y 64 bits:
Código: c
#if CPU_BITS_X86 == 32 && defined(__linux__)
void *mmap_syscall(void *addr, size_t length, int prot, int flags, int fd, off_t offset){
    void *result;
    asm(
        "mov %1, %%eax\n\t"      // Carga el número de llamada del sistema
        "mov %2, %%ebx\n\t"      // addr -> ebx
        "mov %3, %%ecx\n\t"      // length -> ecx
        "mov %4, %%edx\n\t"      // prot -> edx
        "mov %5, %%esi\n\t"      // flags -> esi
        "mov %6, %%edi\n\t"      // fd -> edi
        "mov %7, %%ebp\n\t"      // offset -> ebp
        "int $0x80\n\t"          // Llama al sistema
        "mov %%eax, %0\n\t"      // Guarda el resultado
        : "=r" (result)          // Salida
        : "i" (__NR_mmap), "r" (addr), "r" (length), "r" (prot), "r" (flags), "r" (fd), "r" (offset) // Entradas
        : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "%ebp" // Registros modificados
    );
    return result;
}
#elif CPU_BITS_X86 == 64 && defined(__linux__)
void *mmap_syscall(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
    void *result;
    asm(
        "mov %1, %%rax\n\t"      // Carga el número de llamada del sistema
        "mov %2, %%rdi\n\t"      // addr -> rdi
        "mov %3, %%rsi\n\t"      // length -> rsi
        "mov %4, %%rdx\n\t"      // prot -> rdx
        "mov %5, %%r10\n\t"      // flags -> r10
        "mov %6, %%r8\n\t"       // fd -> r8
        "mov %7, %%r9\n\t"       // offset -> r9
        "syscall\n\t"            // Llama al sistema
        "mov %%rax, %0\n\t"      // Guarda el resultado
        : "=r" (result)          // Salida
        : "i" (__NR_mmap), "r" (addr), "r" (length), "r" (prot), "r" (flags), "r" (fd), "r" (offset) // Entradas
        : "%rax", "%rdi", "%rsi", "%rdx", "%r10", "%r8", "%r9", "memory" // Registros modificados
    );
    return result;
}
#elif defined(__linux__)
#warning  "La arquitectura actual no es de 64bits o 32bits"
void *mmap_syscall(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
    return (void*) syscall(__NR_mmap, addr, length, prot, flags, fd, offset);
}

Los lectores atentos habrán visto que hay una ultima directiva #elif al final, hago uso de esta para, en el caso de que no se pueda detectar el tamaño de palabra como 32 o 64bits, usar un comportamiento default donde se usa syscall y __NR_mmap definidas en cabeceras de C para usar mmap sin tener que hacerlo en asm, podría incluir una condicional mas al elif para que se use esta opción si la maquina en la que se esta compilando no es x86, lo que lo haría mas portable en teoría. Por lo demas, la función mmap_syscall es definida con una versión de 32bits donde se usa registros de 32bits (eax, ebx, ...) y la mas conocida interrupción 80h. En el caso de estar compilándose el código en 64bits(-m64), tenemos una versión que usa los registros de 64bits (rax, rbx, ...) y usamos la instrucción syscall exclusiva de maquinas x86 de 64bits en lugar de int.

Ahora, habréis notado que ambas syscall's, la versión de linux y la versión de windows reciben una serie de argumentos, bastante similares en cuanto a su uso, por ejemplo tenemos un argumento para definir la cantidad de memoria que queremos reservar, lo que seria ¿una pagina de memoria quizas? habría que verlo, pero el campo que nos interesa es ULONG Protect en el caso de "NtAllocateVirtualMemory" o "int prot" en el caso de "mmap". Estos campos se usan para lo mismo y es básicamente para definir los permisos que tiene la "sección" de memoria que queremos pedir al kernel. Esto lo podría especificar el programador y para ello, hemos creado un enum que apenas cambia independientemente de la plataforma para especificar los permisos, aqui podemos verlo, y se situa en la header principal:
Código: c
// permisos permitidos para mmap y NtAllocateVirtualMemory 
typedef enum PermissionMemory 
{
    /*
     *  get_permission_memory:
     *      Obtener los permisos de un bloque.
     *  set_permission_memory:
     *      Cambiar los permisos de un bloque.
     * get_char_permission_memory:
     *      Obtener los permisos de un bloque pero
     *      representado con los caracteres (r,w,x).
     */

    NONE,            // 0 + 0 + 0 = --- = 0
    READ,            // 0 + 0 + 1 = r-- = 1
    WRITE,           // 0 + 0 + 2 = -w- = 2

    READ_WRITE,      // 0 + 1 + 2 = rw- = 3
    EXEC,            // 0 + 0 + 4 = --x = 4
    READ_EXEC,       // 0 + 4 + 1 = r-x = 5
    WRITE_EXEC,      // 0 + 2 + 4 = -wx = 6
    READ_WRITE_EXEC, // 1 + 2 + 4 = rwx = 7

    #ifdef __linux__
    GROWSDOWN = 0x01000000,
    GROWSUP   = 0x02000000,
    SEM       = 0x04000000,
    LOOSE     = 0x10000000,
    HUGETLB   = 0x00040000,
    SAO       = 0x08000000,
    #endif

} PermissionMemory;
Cabe mencionar que estos son permisos de PAGINAS y creo que esto es necesario recalcarlo. Si mal no he visto, estos permisos se especifican de igual manera en Linux como en Windows, que es con valores enteros, 0(nada)(-), 1(leer)(r), 2(escribir)(w) y 4(ejecutar)(x). Los mas linuxeros apreciaran que esto es como los valores octales en chmod. Podemos combinar permisos simplemente sumando valores, por ejemplo para tener permisos de lectura y escritura, sumamos el valor 1(leer) con el valor 2(escribir), lo que da el valor 3(leer y escribir pero no ejecutar). De igual manera podemos dar todos los permisos sumando 1(leer), 2(escribir), 4(ejecutar), lo que da como resultado 7(1+2+4=rwx), lo que se corresponde al 7 de cuando ponemos "chmod 777 <file>" por ejemplo.

Ahora, entremos en declaraciones de los bloques de memoria y etc:
Código: c
typedef struct block_memory
{
    struct
    {                                     // cabecera de los bloques
        _uint64_t id;                     // id del bloque.
        _uint64_t size;                   // tamaño del bloque escluyendo la cabecera. sizeof(addr_memory)
        _uint64_t is_free;                // espacio libre del bloque (size == is_free[el bloque no fue usado])
        struct block_memory *next;        // direcion del siguiente bloque de memoria
        PermissionMemory PermissionBlock; // permisos del bloque.
    } cabecera_bloque;                    // datos de cabecera del bloque
    void *addr_memory;                    // direccion de memoria donde se almacenan los datos del bloque
} block_memory;
#define size_block_memory sizeof(block_memory)


Cabe mencionar que aquí el concepto de "bloque" no es mas que una abstracción de una representación de datos guardados en la misma memoria de la pagina, donde alojamos información de los permisos que tiene una "seccion" que pedimos al allocator, lo que vendria siendo en C el puntero que nos retorna malloc, algo similar, solo que nuestro malloc en teoria debería ser mas complejo. Dichos bloques se alojan en tablas, tablas de bloques valga la redundancia y las tablas se alojan en paginas de memoria, podemos tener una o mas tablas de memoria con distintos permisos y distintos bloques a lo largo de nuestro programa. Y claro, como no, decir que una paginas puede tener mas de una tabla, esto va a depende de si la tabla es mas grande que la pagina, en ese caso necesitaremos mas de una pagina para guardar una tabla. Si la tabla es 1/4 de la pagina, pues en  una misma pagina puede entrar 4 tablas, entonces aqui debemos hacer un diseño razonablemente inteligente mas adelante.

Prosigamos, cada bloque de memoria tiene un header, una cabecera de bloque para que nos entendamos, y esta la hemos visto ya en la definición de block_memory:
Código: c
#define size_block_memory sizeof(block_memory)

typedef struct header
{                                     // cabecera de los bloques
    _uint64_t id;                     // id del bloque
    _uint64_t size;                   // tamaño del bloque escluyendo la cabecera
    _uint64_t is_free;                // espacio libre del bloque (size == is_free[el bloque no fue usado])
    block_memory *next;               // direcion del siguiente bloque de memoria
    PermissionMemory PermissionBlock; // permisos del bloque.
} header;
#define size_header sizeof(header)

En la cabecera guardaríamos un id de 64bits, aunque estoy pensando en cambiar estos valores de 64bits a datos que cambien entre 32, 64 y etc dependiendo del largo de palabra del procesador. Este ID, es un ID relativo entorno a una tabla, que quiero decir con esto. Pues que puede haber mas de un bloque de memoria que tenga el ID 1, mientras se han tablas diferentes, pero en una misma tabla, solo puede haber un bloque con el mismo ID que identifique a este mismo, sera su DNI en la table. Tambien tenemos un campo para indicar el tamaño del bloque, esto lo hacemos para conocer el offset de cada bloque de memoria, y a lo largo de nuestro allocator no pasarnos escribiendo memoria. is_free seria otro valor que indicaría el espacio libre en el bloque. Si size y is_free tienen el mismo valor podemos deducir que el bloque no tiene nada dentro, esta vacio, o que el bloque tiene un tamaño de 0. El tamaño ocupado lo podríamos obtener mediante una simple resta, que seria size-is_free, eso nos daría el tamaño ya ocupado del bloque. El siguiente miembro es next, que es un puntero de la estructura block_memory donde se especificaría la dirección del siguiente bloque de memoria con su propia cabecera. Hay que decir que esto es similar a una lista enlazada, tal vez agregue el que sea una doble lista enlaza, para eso habría que incluir un mismo puntero al bloque anterior. Otro miembro de la cabecera de bloque es el de PermissionBlock, este es un valor el cual indica los permisos que hay en dicho bloque de memoria, hay que decir que sus permisos no pueden ser superior a los permisos de la tabla, eso quiere decir que si la tabla solo tiene permisos de lectura, no podemos hacer que el bloque tenga permisos de ejecución, sino que además todos los bloques de esta solo tendrán permisos de lectura o ninguno, los permisos pueden ser los mismos o inferiores, pero NUNCA SUPERIORES. Esto lo hacemos como un posible mecanismo de seguridad, cabe mencionar que tendremos que ser nosotros luego el que nos aseguremos que no se violen estos permisos.

Ahora, el bloque de memoria aparte del header, tiene un puntero void donde tiene que ser un puntero que cumpla algunas características, como que tenga el tamaño adecuado(size) y que no invada las direcciones de otros bloques, por que en ese caso estaríamos sobrescribiendo bloques y esto es una falla muy gorda. El miembro addr_memory seria como tal el puntero que el programador usara de forma directa u indirecta para lo que el quiera, como puede ser guardar estructuras o leer archivos entre otros.

Veamos como seria la definicion de table_blocks, o tabla de bloques:
Código: c
typedef struct table_blocks
{                                     // tabla de bloques para el allocator
    _uint64_t numbre_blocks;          // cantidad de bloques dentro de la tabla
    block_memory *first;              // primer bloque
    block_memory *last;               // ultimo bloque
    PermissionMemory PermissionTable; // Permisos de la tabla.
    struct table_blocks *next;        // siguiente tabla
} table_blocks;

Los comentarios deben hablar por si mismo, pero aun asi os explicare. Como ya hemos mencionado los bloques se alojan en tablas y las tablas en paginas. numbre_blocks es un miembro donde especificamos la cantidad de bloques, podríamos hacer también un campo donde especificar la cantidad de bloques máxima que podemos tener en la tabla. Otro miembro es first y  last. El primer mencionado es un puntero al primer bloque de la tabla, y el segundo, es un puntero al ultimo bloque, esto tal vez podemos hacerlo para hacer sorting, o simplemente para conocer como se aloja nuestros bloques en la tabla. Hay que mencionar que el miembro last cambiara cada vez que añadamos un bloque nuevo al final de la tabla, y que first cambiara si el bloque al que apunta es eliminado.

PermissionTable podéis deducir que hace(especificar los permisos de la tabla). Y un puntero importante es next, este al igual que en header, hacer referencia a la siguiente tabla, tal vez también este bien hacer una doble lista enlazada.

Luego tendriamos un registro de tablas donde se guardaria las direcciones de cada tabla, este registro tendria que permitir que se actualice elimando y añadiendo tablas, sin que las demas se vean afectadas, actualmente y posiblemente temporalmente esta definida asi:
Código: c
static struct
{
    table_blocks **tablas; // lista donde se almacenan todas las tablas en run time
    _uint64_t n_tablas;    // Numero de tablas almacenadas a lo  largo del programa
} RegistroTablas;

Ahora bien, algunas declaraciones de funciones que hay por ahora:
Código: c
info_os get_info_system();
table_blocks create_table_blocks(ui64 size_memory_resb, PermissionMemory permisos);

bool is_free(block_memory bloque_memoria);

uint64_t get_real_size_block(block_memory bloque_memoria);
uint64_t get_size_block(block_memory bloque_memoria);

block_memory*malloc_c(table_blocks _table_blocks, ui64 size);
size_t create_hash_align(ui8 *data, type_hash type_hash);

void __attribute__((constructor)) __attribute__((visibility("hidden"))) __init1__();
void __attribute__((destructor)) __attribute__((visibility("hidden"))) __end1__();


get_info_system es una función que retorna información acerca de la plataforma actual, esta se retorna en forma de una estructura de tipo info_os la cual varia segun sea windows o linux. Declaraciones:

Para Linux:
Código: c
typedef struct info_os
{
    struct sysinfo info;
    struct utsname sysinfo;
} info_os;

Para Windows:
typedef struct info_os
{
    LPSYSTEM_INFO info;
} info_os;

y aqui su definicion:
Código: c

info_os get_info_system()
{
    info_os data;
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
    // Si el sistema es windows, GetSystemInfo guarda la info posible
    // en la estructura info_os la cual varia entre plataforma.
    GetSystemInfo(data.info);
    /*
     *  void GetSystemInfo([out] LPSYSTEM_INFO lpSystemInfo);
     *
     *  typedef struct _SYSTEM_INFO {
     *
     *      union {
     *          DWORD dwOemId;
     *          struct {
     *
     *              WORD wProcessorArchitecture;
     *              WORD wReserved;
     *
     *          } DUMMYSTRUCTNAME;
     *      } DUMMYUNIONNAME;
     *
     *      DWORD     dwPageSize;
     *      LPVOID    lpMinimumApplicationAddress;
     *      LPVOID    lpMaximumApplicationAddress;
     *      DWORD_PTR dwActiveProcessorMask;
     *      DWORD     dwNumberOfProcessors;
     *      DWORD     dwProcessorType;
     *      DWORD     dwAllocationGranularity;
     *      WORD      wProcessorLevel;
     *      WORD      wProcessorRevision;
     *  } SYSTEM_INFO, *LPSYSTEM_INFO;
     *
     */
#elif __linux__
    /*
     *  #include <sys/sysinfo.h>
     *  int sysinfo(struct sysinfo *info);
     *
     *  Until Linux 2.3.16:
     *  struct sysinfo
     *  {
     *      long uptime;             // Seconds since boot
     *      unsigned long loads[3];  // 1, 5, and 15 minute load averages
     *      unsigned long totalram;  // Total usable main memory size
     *      unsigned long freeram;   // Available memory size
     *      unsigned long sharedram; // Amount of shared memory
     *      unsigned long bufferram; // Memory used by buffers
     *      unsigned long totalswap; // Total swap space size
     *      unsigned long freeswap;  // Swap space still available
     *      unsigned short procs;    // Number of current processes
     *      char _f[22];             // Pads structure to 64 bytes
     *  };
     *
     *  Since Linux 2.3.23 (i386) and Linux 2.3.48 (all architectures) the structure is
     *  struct sysinfo {
     *      long uptime;             // Seconds since boot
     *      unsigned long loads[3];  // 1, 5, and 15 minute load averages
     *      unsigned long totalram;  // Total usable main memory size
     *      unsigned long freeram;   // Available memory size
     *      unsigned long sharedram; // Amount of shared memory
     *      unsigned long bufferram; // Memory used by buffers
     *      unsigned long totalswap; // Total swap space size
     *      unsigned long freeswap;  // Swap space still available
     *      unsigned short procs;    // Number of current processes
     *      unsigned long totalhigh; // Total high memory size
     *      unsigned long freehigh;  // Available high memory size
     *      unsigned int mem_unit;   // Memory unit size in bytes
     *      char _f[20-2*sizeof(long)-sizeof(int)]; //Padding to 64 bytes
     *  };
     *
     *
     *  #include <sys/utsname.h>
     *  int uname(struct utsname *buf);
     *  struct utsname {
     *      char sysname[];    // Operating system name (e.g., "Linux")
     *      char nodename[];   // Name within "some implementation-defined network"
     *      char release[];    // Operating system release (e.g., "2.6.28")
     *      char version[];    // Operating system version
     *      char machine[];    // Hardware identifier
     *      #ifdef _GNU_SOURCE
     *          char domainname[]; // NIS or YP domain name
     *      #endif
     *  };
     */
    // si el sistema es linux, se usa la funcion uname y sysinfo
    // para obtener informacion
    uname(&data.sysinfo);
    sysinfo(&data.info);
#endif
    return data;
}
Como podéis ver hace uso de las funciones de sus propios sistemas para obtener información útil.

La siguiente función seria create_table_blocks, esta recibira un valor de 64bits que seria el tamaño maximo para la tabla de bloques, y un argumento donde se le especifica los permisos de la tabla. Esta funcion se encarga de inicializar una tabla de bloques con los permisos y memoria necesaria. Por ahora la definición sin desarrollar se ve asi:
Código: c
table_blocks create_table_blocks(ui64 size_memory_resb, PermissionMemory permisos)
{
    /*
     * - Esta funcion crea una primera tabla con una
     *      cantidad determinada de buffer(SIZE_TABLE_DEFAULT).
     *
     *  size_memory_resb: memoria a reservar. 0 es reservar
     *                      una tabla con SIZE_TABLE_DEFAULT.
     *                      Un valor distinto impplica reserva
     *                      esa cantidad de bytes.
     */

    if (size_memory_resb == 0)
        size_memory_resb = SIZE_TABLE_DEFAULT;

    // se tiene que almacenar en memoria, y pasarse a tabla
    block_memory bloqueNULL = {
        // falta desarollar:
        //.addr_memory = resb_memory(size_memory_resb); // Esta funcion reserva memoria(conceptualmente)
        .cabecera_bloque = {
            .id = 0,                     // ID 0 para el primer bloque
            .is_free = size_memory_resb, // inicialmente al primer bloque se le asigna la memoria de toda la tabla
            .size = size_memory_resb,
            .next = NULL,           // Aun no se a agregado un primer bloque.
            .PermissionBlock = NONE // Este bloque no tiene ningun tipo de permiso
        }};

    table_blocks tabla = {
        .numbre_blocks = 1,          // numero de bloques iniciales.
        .PermissionTable = permisos, // tabla con los permisos especificados por argumentos
        .first = bloqueNULL,         // primer bloque
        .last = bloqueNULL           // ultimo bloque.
    };
}


La función is_free es de mis favoritas por su simplificad:
Código: c
bool is_free(block_memory bloque_memoria)
{
    /*
     *  - Esta funcion retorna 1 si el bloque esta "vacio" o si tiene algo.
     *  Se recibe un bloque de memoria y se compara el tamano del bloque(size) con la cantidad
     *  de espacio libre(is_free), es deducible que si la cantidad libre es igual al tamano del
     *  bloque quiere decir que el mismo esta vacio, o mejor dicho no contiene nada exceptuando
     * la cabecera.
     */
    if ((bloque_memoria.cabecera_bloque.size._uint64_t - bloque_memoria.cabecera_bloque.is_free._uint64_t) == 0)
        return true;
    else
        return false;
}

Simplemente ejercemos la resta tal como hemos mencionado en la parte de arriba, y si el resultado de la resta es 0 es que esta libre, me recuerda a la resta que hace cmp por alguna razon JSJSJJS.

get_real_size_block daria el tamaño del bloque real, eso quiere decir, que no se te dara el tamaño que como programador pediste, si no una suma de este y el tamaño de la cabecera:
Código: c
#define get_real_size_block_DEF(bloque_memoria) bloque_memoria.cabecera_bloque.size._uint64_t+ size_header

uint64_t get_real_size_block(block_memory bloque_memoria)
{
    /*
     *  - Esta funcion devuelve el tamano real del bloque incluyendo la cabecera del bloque.
     *  Para la operacion se obtendra el tamano ocupado del bloque(size-is_free) y se le suma
     *  el tamano de la cabecera.
     */
    return get_real_size_block_DEF(bloque_memoria);
}
Lo que hacemos es solo sumarle lo que el programador especifico "size" el tamaño del header. De igual manera tenemos get_size_block que retorna el pseudo-tamaño del bloque, un tamaño que el programador pueda usar, ya que get_real_size_block es raro que lo use el usuario, esta función debería usarla mas el allocator para comprender el tamaño real que se esta reservando, ya que en los allocator reales, aunque nosotros en malloc realicemos malloc(16), no se esta reservando únicamente el 16Bytes, sino que internamente esta misma función reserva memoria para sus propias estructuras, solo que como usuario de mas alto nivel no apreciamos esto.

Otra función importante, y que como tal seria la que usualmente quizas usaría el programador:
Código: c
block_memory *malloc_c(table_blocks _table_blocks, ui64 size)
{
    /*
     *
     *  table_blocks _table_blocks: esta estructura es una tabla que contiene los distintos
     *                              datos almacenador por el programa en forma de bloques.
     *                              El programa puede estar formado por un array de tablas
     *                              que contenda cada uno colecciones de bloques de memoria.
     *
     * ui64 size: permite especificar el tamaño de bloque a reservar en la tabla.
     *
     */
    block_memory *ptr = NULL;

    if (_table_blocks.numbre_blocks._uint64_t != 0)
    {
        // aumentar en uno la cantidad de bloques si no esta vacia
        _table_blocks.numbre_blocks._uint64_t++;
    }
    else
    {
        // si la tabla esta vacia
    }

    return ptr;
}

Esta función cogería una tabla que recibe como argumento, y un valor de 64bits(size) el cual seria el tamaño del bloque de memoria. Se me olvido agregar otro para especificar los permisos pero bueno. Esta función se encargaría de entre otras cosas, ver si en la tabla queda memoria para dicho bloque, y en cado contrario retornar NULL. También aumentaría en 1 el tamaño de bloques si el valor no es 0, en ese caso tmb deberíamos actualizar el miembro next del anterior bloque, al bloque actual por ejemplo. En el caso de que la tabla este vacia, la inicializamos si no lo esta, y en next colocaríamos NULL pues no hay bloque de memoria al que apuntar o como mucho, le pondríamos que apunte a el mismo.


Ahora, una función a tener en cuenta es create_hash_align, esta función es algo característica. Esta se encarga de que, dada x entrada(data), se cree un hash que puede estar alineado en memoria y del largo de palabra del procesador, independientemente de la función hash usada. Cabe mencionar que la utilidad de esta función no es completamente criptografica, se usara mas para la comprobación de errores por ejemplo, o para la detención de alteraciones de memoria, seria algo así como nuestro checksum y que este alineado con la memoria seria opcional.

Todo se especifica mediante de un enum type_hash:
Código: c
typedef struct type_hash
{
    enum
    {
        MurmurHash128Bits_x86_32,
        MurmurHash128Bits_x86_64,
        MurmurHash32Bits,
        MD5,
        md4,
        sha256
    } type_hash;
    enum
    {
        no,
        yes
    } create_hash_align;
    union
    {
        ui64 hash_128bits[2];
        ui32 hash_32bits;
        ui16 hash_16bits;
    } hash;

} type_hash;

en el primer miembro de type_hash, el miembro type_hash, se usa para especificar el algoritmo hash que he implementado a usar. cabe mencionar que MurmurHash128Bits_x86_64 y MurmurHash128Bits_x86_32 son versiones de Murmur hash de claves de 128bits optimizadas para sus correspondientes arquitecturas. Tenemos MurmurHash32Bits que seria una versión de la misma pero con una clave de 32 bits, la posibilidad de usar la función MD5 y MD4 y la conocida función SHA256.

El siguiente campo es create_hash_align donde se le especifica si queremos un hash del largo de palabra del procesador para su alineación en memoria o si preferimos que el largo se defina por la función y el miembro hash.

El miembro hash, es un miembro donde se guarda el hash si no es alineado, en caso de que el hash este alineado, se retorna desde la función como un valor size_t. Estos campos miembros se define para claves de 16, 32 y 128 bits que son las que necesarias para las funciones hash. Decir que la función create_hash_align  no esta terminada:
Código: c
size_t create_hash_align(ui8 *data, type_hash type_hash)
{
    /*
     * Se creara una func hash que recibe un vector.
     * El vector a de ser mayor o igual que el tamano
     * de palabra del procesador. En el caso de no ser asi
     * se usara un padding de 0's para llegar a completar el vector.
     * Una vez se tiene el vetor, se le aplicar una funcion H() para
     * obtener un hash h. h = H(x). Donde x es el vector.
     * h sera del tamano de palabra del procesador, es decir, en un
     * procesador de 32bits, h es de 32/8bits=4 y en uno de 64bits
     * h es 64/8=8bytes.
     *
     * 32bits => 0000_0000.0000_0000
     *
     * 32bits => 0000_0000.0000_0000.0000_0000.0000_0000
     *
     * 64bits => 0000_0000.0000_0000.0000_0000.0000_0000
     *           0000_0000.0000_0000.0000_0000.0000_0000
     */

#if CPU_BITS_X86 == 64
#define SIZE_WORD 8
    ui8 hash[SIZE_WORD] = {0, 0, 0, 0, 0, 0, 0, 0};
#elif CPU_BITS_X86 == 32
#define SIZE_WORD 4
    ui8 hash[SIZE_WORD] = {0, 0, 0, 0};
#elif CPU_BITS_X86 == 16
#define SIZE_WORD 2
    ui8 hash[SIZE_WORD] = {0, 0};
#endif
    if (type_hash.create_hash_align == yes)
    {
        ui32 size = ((_uint32_t)strlen_c((string_c)data))._uint32_t;
        ui32 size_ = size + ((SIZE_WORD - (size % SIZE_WORD)) % SIZE_WORD);

        switch (type_hash.type_hash)
        {
        case MurmurHash128Bits_x86_32:
#ifdef __DEBUG__
            printf("Aplicando MurmurHash128Bits_x86_32\n");
#endif
            MurmurHash3_x32_128(data, size, size, type_hash.hash.hash_128bits);
            /*
            uint32_t value = 0xaabbccdd;
            uint8_t num[4];
            memcpy(num, &value, sizeof(value));
            */
            data = (ui8 *)type_hash.hash.hash_128bits;
            size = sizeof(type_hash.hash.hash_128bits);
            size_ = size + ((SIZE_WORD - (size % SIZE_WORD)) % SIZE_WORD);
#ifdef __DEBUG__
            printf("hash %llx%llx\n", type_hash.hash.hash_128bits[0], type_hash.hash.hash_128bits[1]);
#endif
            break;
        case MurmurHash128Bits_x86_64:
#ifdef __DEBUG__
            printf("Aplicando MurmurHash128Bits_x86_64\n");
#endif
            MurmurHash3_x64_128(data, size, size, type_hash.hash.hash_128bits);
            data = (ui8 *)type_hash.hash.hash_128bits;
            size = sizeof(type_hash.hash.hash_128bits);
            size_ = size + ((SIZE_WORD - (size % SIZE_WORD)) % SIZE_WORD);
#ifdef __DEBUG__
            printf("hash %llx%llx\n", type_hash.hash.hash_128bits[0], type_hash.hash.hash_128bits[1]);
#endif
            break;
        case MurmurHash32Bits:
#ifdef __DEBUG__
            printf("Aplicando MurmurHash32Bits\n");
#endif
            type_hash.hash.hash_32bits = murmur3_32(data, size, size);
            data = (ui8 *)type_hash.hash.hash_32bits;
            size = sizeof(type_hash.hash.hash_32bits);
            size_ = size + ((SIZE_WORD - (size % SIZE_WORD)) % SIZE_WORD);
#ifdef __DEBUG__
            printf("hash %x\n", type_hash.hash.hash_32bits);
#endif
            break;
        case MD5:
            ui8 *a = (ui8 *)&type_hash.hash.hash_32bits;
#ifdef __DEBUG__
            printf("Aplicando md5\n");
#endif

            md5(data, (_uint32_t){size}, a);
            data = a;
            size = sizeof(type_hash.hash.hash_32bits);
            size_ = size + ((SIZE_WORD - (size % SIZE_WORD)) % SIZE_WORD);
#ifdef __DEBUG__
            printf("hash %x\n", type_hash.hash.hash_32bits);
#endif
            break;
        case md4:
#ifdef __DEBUG__
            printf("Aplicando md4\n");
#endif
            break;
        case sha256:
#ifdef __DEBUG__
            printf("Aplicando sha256\n");
#endif
            break;
            /*default MurmurHash128Bits_x86_64:
                break;*/
        }

#ifdef __DEBUG__

        printf("Aplicando create_hash_align, size: %d %d\n", size, size_);
#endif
        for (ui32 i = 0; i < size_; i++)
        {

            if (size < i)
            {
                hash[i % SIZE_WORD] = XOR_operator(hash[i % SIZE_WORD], 0b01010101); // data[i];//
            }
            else
            {
                hash[i % SIZE_WORD] = XOR_operator(hash[i % SIZE_WORD], data[i + 1]); // data[i];//
                hash[i % SIZE_WORD + 1] = XOR_operator(hash[i % SIZE_WORD], data[i]); // data[i];//
            }

#if CPU_BITS_X86 == 64 && __DEBUG__
            printf("HASH bin \t-> " bin_pattern_ui64 " i = %d %d %c\n", bin_ui64(*((size_t *)hash)), i % SIZE_WORD, i, data[i]);
#elif CPU_BITS_X86 == 32 && __DEBUG__
            printf("HASH bin \t-> " bin_pattern_ui32 " i = %d %d %c\n", bin_ui32(*((size_t *)hash)), i % SIZE_WORD, i, data[i]);
#elif CPU_BITS_X86 == 16 && __DEBUG__
            printf("HASH bin \t-> " bin_pattern_ui16 " i = %d %d %c\n", bin_ui16(*((size_t *)hash)), i % SIZE_WORD, i, data[i]);
#endif
        }
    }
    return *((size_t *)hash);
}

Esto lo hacemos, por que alinear la memoria mejora el acceso a la memoria, permite que se acceda con mayor velocidad que es algo importante en un allocator.

Para alinear una direccion de memoria o un valor o cualquier otro podemos usar la siguiente formula:
Código: text
#define MEMORY_ALIGN(to_be_aligned) AND_operator((to_be_aligned + sizeof(size_t) - 1), NOT_operator(sizeof(size_t) - 1));
básicamente es hacer una operación NOT y AND con un par de resta y una suma.

Para crear un hash de tamaño de palabra de procesador, se crea un array hash con el tamaño que se define por SIZE_WORD, este valor cambia si se compila para 16bits(-m16), 32bits(-m32) o 64bits(-m64) y se define haciendo uso de la macro CPU_BITS_X86 que se define en una header personal he indica la cantidad de bits del procesador.

Este array se inicializa en 0, usamos la funcion hash especificada por argumento y la guardamos en un segundo array, y aposterior realizamos una serie de operaciones XOR con los valores del hash para obtener un hash de tamaño constante, independientemente del tamaño del hash de entrada.

Tengo que decir, que este codigo no os funcionara por varias razones, algunas de ellas es que obviamente es código de POC, es decir, mayormente son comentarios diciendo que debería hacerse, definiciones de funciones, estructuras y etc. Pero aparte, usa una serie de headers que son propias y no puedo seguir mostrando aqui por que este post daria para un pequeño libro de un par de cuantas paginas, pero son las siguientes como curiosidad:
Código: php
#include "MurmurHash.h"
#include "md4.h"
#include "md5.h"
#include "sha256.h"
#include "string_c.h"
#include "type_data_c.h"
#include "operators_logics.h"
#include "ErrorAllocator.h"
Las primeras 4 son implementaciones propias de los distintos hash's. string_c es una cabecera personal para manejar arrays de forma dinámica en C. operators_logics.h seria una cabecera donde se define operaciones binarias a nivel de Bits como son OR, XOR, NAND, desplazamiento de bits izquierdos y derechos y etc. Ademas de definicion de macros para la impresion de numeros en formato binario(poco optimizado). Una header importante type_data_c.h donde defino entre otras cosas tipos de datos propios multiplataforma y que no cambian de tamaño independientemente del sistema, compilador o tamaño de palabra del procesador, se define macros para identificar la arquitectura, saber si la maquina es little o big endian, definir limites de tipos de datos, definiciones estándares como las de stdbool.h pero propias, definiciones de tipos de datos de windows, por si queremos usar cosas como DWORD en codigos no destinados a windows. definición de tipos de datos tipo NASM, con db, dw, dd, o dq. definición de estructuras de datos de tamaño constante, definición de estructuras anonimas para multi-tipos de datos, definición para la obtención del byte alto y bajo y otras
#3
No tienes permitido ver enlaces. Registrate o Entra a tu cuenta
No tienes permitido ver enlaces. Registrate o Entra a tu cuenta
No tienes permitido ver enlaces. Registrate o Entra a tu cuenta
                             

Hola, este es mi tercer post, en esta ocasión les traigo el Servidor con Socket 2.0 con mayor seguridad para experimentar y practicar tus broteforces.
≡¿Por que haces esto si no son buenos códigos?                                               
≡ .1 Hago esto para la gente que recién esta empezando en estos temas, así     
≡  experimentan y aprenden, no son tan buenos códigos por que no es necesario.
≡¿Harás otra parte con el servidor con mas seguridad?                                     
≡ .2 Posiblemente, todo depende de mi tiempo y ganas.
                                   
[move]- Gracias a mi amigo M4 por darme permiso de usar la función de captcha, créditos para él.[/move]




Me lo intente currar un poco para que realmente les pueda ayudar, si comentan y dan su opinión lo agradecería mucho.
Si tienen algún problema con el código no duden en comentarme para que se los resuelva, posiblemente de error por la tabulación, aseguraos de ponerlos bien jeje
si gustan pueden poner en los comentarios que quieren que traiga la próxima vez, si se hacerlo y dispongo del tiempo se los puedo traer
Versión 1.0 del Servidor: No tienes permitido ver enlaces. Registrate o Entra a tu cuenta

Código: python
#!/usr/bin/env python
import socket,os,random,string 

host="0.0.0.0" 
port=1879 
password="connor"  

s=socket.socket(socket.AF_INET,  socket.SOCK_STREAM) 
s.bind((host, port)) 

def clear(): #La famosa funcion
    if os.name == "nt":
        os.system("cls")
    else:
        os.system("clear")

clear()

print("{*} Servidor encendido.")
sgd = 1 

def capcha():  #funcion del captcha < Creditos a M4
	a=random.choices(string.ascii_uppercase, k=4)
	print("Capcha solicitado "+"".join(a))
	return a
while 1:
    s.listen(2) 
    conn, addr = s.accept()
    clip = addr 
    print("{*} Alguien se ha conectado al servidor: "+str(clip)+"\n") 

    capvalue="".join(capcha()) 
    conn.send(capvalue.encode("utf8")) 
    conn.send("\nCapcha> ".encode("utf8")) 
    try:
        capclient=conn.recv(1024).decode("utf8") 
        capclient=capclient.replace("\n","")  
        if str(capclient)==capvalue: 
            print("{*} El usuario entro con el captcha correcto.")
            conn.send("{*} Captcha correcto\n".encode("utf8")) 
            while True:
                conn.send("Pass> ".encode("utf8")) 
                data=conn.recv(1024).decode("utf8") 
                data=data.replace("\n", "") 
                if data==password or str(data)==password:
                    conn.send("GG, pass correcta".encode("utf8")) 
                    print("El usuario uso la contraseña correcta")
                    break
                else:
                    print("Un user fracaso al poner la contraseña: "+data+"\nIntento numero: "+str(sgd)+"\n") 
                    sgd = sgd + 1
                    if sgd==5:
                        conn.send("{*} El Servidor se cierra por ataque de bruteforce".encode("utf8"))
                        print("{*} El servidor fue detenido por ataque de bruteforce\n")
                        print("{*} La IP del posible atacante: " + str(clip))
                        break
                        conn.close()
                        s.close()
                    else:
                        continue
                        conn.send("Contraseña incorrecta\n".encode("utf8"))
        else:
            print("El usuario ha fallado el capcha:"+ capclient)
            conn.send("Failed capcha\n".encode("utf8"))
            conn.close()
    except:
        print("El user se ha desconectado")
        conn.close()

Sé que no es el mejor código del mundo, pero sirve para practicar, el próxima hare algo que sea  mas parecido a un hacker de películas, ya verán jaja, quizás me tarde mas en terminarlo y etc.
Espero que te guste el código y adiós, cuidate.  ;) ;D

Edit: Si no sabes como hacer el cliente acá te dejo la base y te explico mas o menos de que va, en la proxima versión que haga explicare a detalle.

Código: python
import socket #Importamos Socket, es importante ya que sin eso basicamente no funcionaria nada

host="0.0.0.0" #Aca es donde ponemos la IP, ojo, tenes que poner la misma que pusiste en el servidor, pero igual es mejor dejarlo como esta

port="24" #Lo mismo con la IP, si quieren este pueden cambiarlo, si lo cambian y dice algo como "Permiso denegado" es que no les deja usar ese puerto

s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) #Base del socket (no recomiendo tocar, pero si quieren pueden buscar mejores opciones)
 s.connect((host, port)) #Esto da la orden que se tiene que conectar al puerto e ip


#Datos extras:
#Recomiendo usar nc para ver el servidor como se muestra en la primera imagen en kali linux, se usa de este modo: nc (IP) (PUERTO) ejemplo: nc 0.0.0.0 80
#Recomiendo que si tienen algun fallo o duda me lo dejen en los comentarios ya que se intentare echarles un cable
#Lo mas posible es que de error en los codigos por que quizas esten mal tabulados, ya que al copiar y pegar se modifican las tabulaciones para cualquier lado
#Recuerda que socket es un modulo del cual es algo complicado de usar, pero con tiempo y practica se puede dominar, no todo sale a la primera, lo mas probable es que de error al principio tu cliente
#Tambien recuerda que 0.0.0.0 es igual a localhost, es decir, tu amigo no podra ejecutar el servidor y vos hacer el bruteforce, para eso estan cosas como ngrok del cual podes buscar tutoriales
#de como usarlo, pero no recomiendo usarlo, ya que ngrok para hacer bruteforce al enviar muchas peticiones se cierra


Hola bro, un placer, me gustaría decirte que hay mejores maneras de limpiar la pantalla que usando el comando cls o clear, puedes investigar, sobre el modulo colorama que te permite hacer esto directamente y sin tener que averiguar en que plataforma se esta corriendo tu codigo. Tambien tengo que mencionar que uses las clausulas try-except de forma un poco irresponsable, quiero decir, cualquier error del tipo que sea que ocurra dentro de estas clausulas lo reportara como que el usuario fallo el catchap y le cierras la misma conexion. Esto no es correcto, ya que si por ejemplo, el cliente manda un buffer de mas de 1024 bytes, como tu en tu server has puesto.un .recv(1024), esto generara un error, y aunque el cliente haya acertado el catchap(cosa que no tendria sentido hacer un catchap mayor al que soporta el buffer de tu server), le procederas a cerrar la conexion independientemente de si acerto o lo fallo, simplemente por que no contemplas que tu codigo tenga la posibilidad de fallar alli. Mi recomendacion es que uses esta clausula de forma mas responsable especificando a except el tipo de error que quieres capturar, puedes capturar dos errores al mismo tiempo para solo tener que usar una clausula try-except a la vez con la misma funcionalidad en el except, lo que seria algo asi:
Código: python
try:
    # code
except (name_error1, name_error2):
    # code
Yo personalmente pienso que no es bueno usar try-catch tal como lo haces, pienso que es una mala practica y hace que no comprendas completamente los errores que se pueden generar en dicha porcion de codigo, ademas de que pones un comportamiento default para cualquier tipo de error que se genere en respuesta.

Ahora, otra cosa que podemos hacer para mejorar este codigo es hacer al servidor multithreading, ya que si un cliente se conecta al servidor y se queda hay esperando, y mientras, otro cliente intenta conectarse. El segundo cliente no podra establecer una conexion por que el servidor aun esta atendiendo al cliente1. Por esta parte podrias hacer uso del metodo .timeout si mal no recuerdo tmb, que te permite especificar cuanto tiempo puede estar un cliente conectado sin hacer nada, por defecto creo recordar que este valor es 0, por lo que es un tiempo indefinido, y esto se podria usar tmb para explotar tu servidor, ya que puedes establecer multiples conexiones contra tu servicio y hacer nada, lo que a la larga haria que el server no pueda aceptar mas conexiones y dejes a clientes reales sin la posibilidad de interactuar con tu servicio.
Tambien deberia haber un try-catch adecuado para bind ya que si el puerto que va a usar tu servicio ya esta siendo usado por otro,tu codigo dara un error en dicha linea.

Como curiosidad hay que decir que no es necesario que hagas esto:
Código: python
socket.socket(socket.AF_INET,  socket.SOCK_STREAM) 

 Puedes hacer esto simplemente que hace lo mismo ya que el constructor de socket toma esos valores por defecto:
Código: python
socket.socket() 
Si quisieras hacer una conexion UDP o de otro tipo, ahi si tendrias que meterle los valores necesarios al constructor para realizar un socket del tipo que quieres.

No es necesario que hagas esto:
Código: text
str(capclient)
Por que al usar el metodo .decode ya lo transforma a str.
Luego tambien, en un servidor algo mas real, bloquearias al usuario mediante su direccion IP y otros datos que este normalmente te reportaria, puedes hacer que tu servidor necesite tener registrado en una base de datos una serie de clientes con sus nombres para realizar una conexion exitosa, y que el cliente tenga que especificar su nombre para poder acceder y de caso contrario pues simplemente le cierras la conexion. Si el usuario falla la password podrias bloquearle la conexión por su nickname simplemente.
Espero haberte ayudado

Hola hermano, buenos días. Este código era una beta de hace tiempo, mi manera de programar es primero hacerlo funcional y luego pulirlo al máximo;
Agradezco mucho tu corrección, es de gran ayuda, saludos bro!

O comprendo, entonces discúlpame por no haberlo interpretado como tal o por si lo habías indicado en alguno de tus pots, por no leerlo, a la proxima lo tengo en cuenta, sabiendo ya que eres tu :D
#4
No tienes permitido ver enlaces. Registrate o Entra a tu cuentaHola que tal chic@s, hace algun tiempo programe un Script el cual nos muestran
un listados de todos los dispositivos conectados a nuestra red, el Script no solo
nos muestra las direcciones ip y mac, sino el tipo de dispositivo o Hardware.

En el repositorio hay 2 Script, scannerdevice_api.py usa una api web para
traducir las direcciones MAC luego esta No tienes permitido ver enlaces. Registrate o Entra a tu cuenta el cual usa un modulo para
el proceso.

Ambos Script funcionan perfectamente y cumple su objetivo, espero que le sea de mucha
ayuda para que lo integren a algunos proyectos que tienen por hay ;)

USO:
Código: text
sudo ./script.py 192.168.0.0/24

DEMO:


REPOSITORIO:
No tienes permitido ver enlaces. Registrate o Entra a tu cuenta


Muy bueno bro, le di start a tu repo. En su momento hice tmb algo similar con scapy, gran modulo por cierto, aqui te lo dejo por si quieres echarle un vistazo al mio, aunque hace time que no le toco y no lo acabe:
No tienes permitido ver enlaces. Registrate o Entra a tu cuenta

Aqui puedes contemplar alguna de las cosas que le meti al script:
Código: php
usage: ToolNetAnalysis.py [-h] [-if--iface IF__IFACE] [--verbose]
                          [-ip-r--ip-range IP_R__IP_RANGE]
                          [--ip-objetivo IP_OBJETIVO] [--ping-spoof]
                          [--ip-spoof IP_SPOOF] [--mac-spoof MAC_SPOOF]
                          [--timeout TIMEOUT] [--ttl-packet TTL_PACKET]
                          [--ttl-packet-random TTL_PACKET_RANDOM TTL_PACKET_RANDOM]
                          [--count COUNT] [--scan-tcp-random] [--scan-mode-arp]
                          [--iface-list] [--sniff]

Esta es una herramienta de analisis y reconocimiento

options:
  -h, --help            show this help message and exit
  -if--iface IF__IFACE  Especificar la tarjeta de red con la que operar
  --verbose, -v         El modo verbose muestra informacion de los procesos
                        internos
  -ip-r--ip-range IP_R__IP_RANGE
                        Rango de red objetivo junto a mascara de red.
                        Ejemplo(192.168.1.1/24)
  --ip-objetivo IP_OBJETIVO, -ip-obj IP_OBJETIVO
                        IP objetivo a la que realizar el ataque o analizar
  --ping-spoof, -ping-sp
                        Realizar un ping spoofing. Se puede especificar el ttl o
                        usar ttl aleatorio. Se a de especificar la IP de
                        destino(Objetivo). Opcionalmente se puede especificar la
                        IP de origen. Se puede spoofear la direccion IP de
                        origen con --ip-spoof
  --ip-spoof IP_SPOOF, -ip-sp IP_SPOOF
                        En este campo se puede especificar la direccion IP a
                        spoofear
  --mac-spoof MAC_SPOOF, -mac-sp MAC_SPOOF
                        En este campos se puede especidiar la direccion MAC que
                        se usara para spoofear
  --timeout TIMEOUT, -t TIMEOUT
                        tiempo de espera para le escucha de paquetes.
  --ttl-packet TTL_PACKET, -ttl TTL_PACKET
                        En este campo se puede especificar el ttl de los
                        paquetes, por defecto se usa 128
  --ttl-packet-random TTL_PACKET_RANDOM TTL_PACKET_RANDOM, -ttl-rand TTL_PACKET_RANDOM TTL_PACKET_RANDOM
                        Esta flag especifica que se usara ttl aleatorio para los
                        paquetes. Se a de especifica el rango aleatorio para el
                        ttl en esta flag de la siguiente manera --ttl-packet-
                        random <rango inicial> <rango final>
  --count COUNT, -c COUNT
                        Esta flag permite especificar el count, por ejemplo. En
                        icmp spoof, usando count 5 se puede realizar 5
                        peticiones ICMP spoofeadas. Por defecto count vale 1.
  --scan-tcp-random, -sc-tcp-rand
                        Escanea los puertos aleatorios de una red o dispositivo
  --scan-mode-arp, -sc-arp
                        Escanear la red haciendo uso del protocolo ARP
  --iface-list, -if-list
                        Listar interfaces las interfaces de red del dispositivo
                        en el que se esta trabajando
  --sniff, -sn          Sniffear la red. Puede usar --iface para especificar que
                        tarjeta usar para realizar el sniffing

#5
No tienes permitido ver enlaces. Registrate o Entra a tu cuenta
                             

Hola, este es mi tercer post, en esta ocasión les traigo el Servidor con Socket 2.0 con mayor seguridad para experimentar y practicar tus broteforces.
≡¿Por que haces esto si no son buenos códigos?                                               
≡ .1 Hago esto para la gente que recién esta empezando en estos temas, así     
≡  experimentan y aprenden, no son tan buenos códigos por que no es necesario.
≡¿Harás otra parte con el servidor con mas seguridad?                                     
≡ .2 Posiblemente, todo depende de mi tiempo y ganas.
                                   
[move]- Gracias a mi amigo M4 por darme permiso de usar la función de captcha, créditos para él.[/move]




Me lo intente currar un poco para que realmente les pueda ayudar, si comentan y dan su opinión lo agradecería mucho.
Si tienen algún problema con el código no duden en comentarme para que se los resuelva, posiblemente de error por la tabulación, aseguraos de ponerlos bien jeje
si gustan pueden poner en los comentarios que quieren que traiga la próxima vez, si se hacerlo y dispongo del tiempo se los puedo traer
Versión 1.0 del Servidor: No tienes permitido ver enlaces. Registrate o Entra a tu cuenta

Código: python
#!/usr/bin/env python
import socket,os,random,string 

host="0.0.0.0" 
port=1879 
password="connor"  

s=socket.socket(socket.AF_INET,  socket.SOCK_STREAM) 
s.bind((host, port)) 

def clear(): #La famosa funcion
    if os.name == "nt":
        os.system("cls")
    else:
        os.system("clear")

clear()

print("{*} Servidor encendido.")
sgd = 1 

def capcha():  #funcion del captcha < Creditos a M4
	a=random.choices(string.ascii_uppercase, k=4)
	print("Capcha solicitado "+"".join(a))
	return a
while 1:
    s.listen(2) 
    conn, addr = s.accept()
    clip = addr 
    print("{*} Alguien se ha conectado al servidor: "+str(clip)+"\n") 

    capvalue="".join(capcha()) 
    conn.send(capvalue.encode("utf8")) 
    conn.send("\nCapcha> ".encode("utf8")) 
    try:
        capclient=conn.recv(1024).decode("utf8") 
        capclient=capclient.replace("\n","")  
        if str(capclient)==capvalue: 
            print("{*} El usuario entro con el captcha correcto.")
            conn.send("{*} Captcha correcto\n".encode("utf8")) 
            while True:
                conn.send("Pass> ".encode("utf8")) 
                data=conn.recv(1024).decode("utf8") 
                data=data.replace("\n", "") 
                if data==password or str(data)==password:
                    conn.send("GG, pass correcta".encode("utf8")) 
                    print("El usuario uso la contraseña correcta")
                    break
                else:
                    print("Un user fracaso al poner la contraseña: "+data+"\nIntento numero: "+str(sgd)+"\n") 
                    sgd = sgd + 1
                    if sgd==5:
                        conn.send("{*} El Servidor se cierra por ataque de bruteforce".encode("utf8"))
                        print("{*} El servidor fue detenido por ataque de bruteforce\n")
                        print("{*} La IP del posible atacante: " + str(clip))
                        break
                        conn.close()
                        s.close()
                    else:
                        continue
                        conn.send("Contraseña incorrecta\n".encode("utf8"))
        else:
            print("El usuario ha fallado el capcha:"+ capclient)
            conn.send("Failed capcha\n".encode("utf8"))
            conn.close()
    except:
        print("El user se ha desconectado")
        conn.close()

Sé que no es el mejor código del mundo, pero sirve para practicar, el próxima hare algo que sea  mas parecido a un hacker de películas, ya verán jaja, quizás me tarde mas en terminarlo y etc.
Espero que te guste el código y adiós, cuidate.  ;) ;D

Edit: Si no sabes como hacer el cliente acá te dejo la base y te explico mas o menos de que va, en la proxima versión que haga explicare a detalle.

Código: python
import socket #Importamos Socket, es importante ya que sin eso basicamente no funcionaria nada

host="0.0.0.0" #Aca es donde ponemos la IP, ojo, tenes que poner la misma que pusiste en el servidor, pero igual es mejor dejarlo como esta

port="24" #Lo mismo con la IP, si quieren este pueden cambiarlo, si lo cambian y dice algo como "Permiso denegado" es que no les deja usar ese puerto

s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) #Base del socket (no recomiendo tocar, pero si quieren pueden buscar mejores opciones)
 s.connect((host, port)) #Esto da la orden que se tiene que conectar al puerto e ip


#Datos extras:
#Recomiendo usar nc para ver el servidor como se muestra en la primera imagen en kali linux, se usa de este modo: nc (IP) (PUERTO) ejemplo: nc 0.0.0.0 80
#Recomiendo que si tienen algun fallo o duda me lo dejen en los comentarios ya que se intentare echarles un cable
#Lo mas posible es que de error en los codigos por que quizas esten mal tabulados, ya que al copiar y pegar se modifican las tabulaciones para cualquier lado
#Recuerda que socket es un modulo del cual es algo complicado de usar, pero con tiempo y practica se puede dominar, no todo sale a la primera, lo mas probable es que de error al principio tu cliente
#Tambien recuerda que 0.0.0.0 es igual a localhost, es decir, tu amigo no podra ejecutar el servidor y vos hacer el bruteforce, para eso estan cosas como ngrok del cual podes buscar tutoriales
#de como usarlo, pero no recomiendo usarlo, ya que ngrok para hacer bruteforce al enviar muchas peticiones se cierra


Hola bro, un placer, me gustaría decirte que hay mejores maneras de limpiar la pantalla que usando el comando cls o clear, puedes investigar, sobre el modulo colorama que te permite hacer esto directamente y sin tener que averiguar en que plataforma se esta corriendo tu codigo. Tambien tengo que mencionar que uses las clausulas try-except de forma un poco irresponsable, quiero decir, cualquier error del tipo que sea que ocurra dentro de estas clausulas lo reportara como que el usuario fallo el catchap y le cierras la misma conexion. Esto no es correcto, ya que si por ejemplo, el cliente manda un buffer de mas de 1024 bytes, como tu en tu server has puesto.un .recv(1024), esto generara un error, y aunque el cliente haya acertado el catchap(cosa que no tendria sentido hacer un catchap mayor al que soporta el buffer de tu server), le procederas a cerrar la conexion independientemente de si acerto o lo fallo, simplemente por que no contemplas que tu codigo tenga la posibilidad de fallar alli. Mi recomendacion es que uses esta clausula de forma mas responsable especificando a except el tipo de error que quieres capturar, puedes capturar dos errores al mismo tiempo para solo tener que usar una clausula try-except a la vez con la misma funcionalidad en el except, lo que seria algo asi:
Código: python
try:
    # code
except (name_error1, name_error2):
    # code
Yo personalmente pienso que no es bueno usar try-catch tal como lo haces, pienso que es una mala practica y hace que no comprendas completamente los errores que se pueden generar en dicha porcion de codigo, ademas de que pones un comportamiento default para cualquier tipo de error que se genere en respuesta.

Ahora, otra cosa que podemos hacer para mejorar este codigo es hacer al servidor multithreading, ya que si un cliente se conecta al servidor y se queda hay esperando, y mientras, otro cliente intenta conectarse. El segundo cliente no podra establecer una conexion por que el servidor aun esta atendiendo al cliente1. Por esta parte podrias hacer uso del metodo .timeout si mal no recuerdo tmb, que te permite especificar cuanto tiempo puede estar un cliente conectado sin hacer nada, por defecto creo recordar que este valor es 0, por lo que es un tiempo indefinido, y esto se podria usar tmb para explotar tu servidor, ya que puedes establecer multiples conexiones contra tu servicio y hacer nada, lo que a la larga haria que el server no pueda aceptar mas conexiones y dejes a clientes reales sin la posibilidad de interactuar con tu servicio.
Tambien deberia haber un try-catch adecuado para bind ya que si el puerto que va a usar tu servicio ya esta siendo usado por otro,tu codigo dara un error en dicha linea.

Como curiosidad hay que decir que no es necesario que hagas esto:
Código: python
socket.socket(socket.AF_INET,  socket.SOCK_STREAM) 

 Puedes hacer esto simplemente que hace lo mismo ya que el constructor de socket toma esos valores por defecto:
Código: python
socket.socket() 
Si quisieras hacer una conexion UDP o de otro tipo, ahi si tendrias que meterle los valores necesarios al constructor para realizar un socket del tipo que quieres.

No es necesario que hagas esto:
Código: text
str(capclient)
Por que al usar el metodo .decode ya lo transforma a str.
Luego tambien, en un servidor algo mas real, bloquearias al usuario mediante su direccion IP y otros datos que este normalmente te reportaria, puedes hacer que tu servidor necesite tener registrado en una base de datos una serie de clientes con sus nombres para realizar una conexion exitosa, y que el cliente tenga que especificar su nombre para poder acceder y de caso contrario pues simplemente le cierras la conexion. Si el usuario falla la password podrias bloquearle la conexión por su nickname simplemente.
Espero haberte ayudado
#6
No tienes permitido ver enlaces. Registrate o Entra a tu cuentaEl siguiente script de Python usa la solicitud para descargar archivos y los escribe en el sistema de archivos, y usa el módulo de la barra de progreso para mostrar la barra de progreso de la descarga.

Entre ellos, el archivo descargado por el módulo de solicitud se puede descargar directamente sin usar el método abierto.

Creo que no has agregado el codigo si es que querias que vieramos algo😅
#7
No tienes permitido ver enlaces. Registrate o Entra a tu cuenta

★゜・。。・゜゜・。。・゜☆゜・。。・゜゜・。。・゜★

Buenas a todos, en esta ocasión les traigo un pequeño código que adapté para que un bot les envíe las contraseñas guardadas en Chrome WIN10 de quien ejecute el bot.
IMPORTANTE el código esta en MUY temprano desarrollo, es decir, que puede que tenga muchos fallos y no esté programado ni ideado de la manera más optima, pero tranquilos que en la próxima versión solucionaré muchas cosas.
Para las versiones futuras planeo que el usuario no tenga que ejecutar el bot, sino simplemente un código, que con comandos puedas abrir un menú donde haya múltiples sesiones, sea más seguro, más oculto, persistencia, etc.
Simplemente hago este código por entretenimiento y educar, es obvio que en la altura en que lo está, es imposible o muy difícil que alguien caiga en la trampa, tampoco ese es mi objetivo.
Para finalizar y sin más rodeo, les invito que si quieren pueden darme ideas o cosas que quieran que implemente al mini proyecto:
Me encantaría que lo modifiquen y hagan sus propias versiones, mejorándolo al máximo; Como dije, tengo muchas ideas y cosas por mejorar, solamente pido paciencia ya que no cuento con todo el tiempo del mundo.

PASOS A SEGUIR:

Creamos un bot en el portal de Discord Developer, uno simple, no importa las características, el mío es así:



Lo invitamos a un server, agarramos su token y apretamos para enviarle un mensaje privado.
Colocamos el Token en que se encuentra al final del código.

Link del código: No tienes permitido ver enlaces. Registrate o Entra a tu cuenta
Contraseña: BOTDISCORDBETA

Ejecutamos el código y descargamos las dependencias, si tienen algún error digan en los comentarios.

Algo así debería de aparecerles, vayan y pulsen en mandarles mensaje privado.
Una vez allí, escriban la frase secreta y poderosa: $pw
Automáticamente les empezará a enviar unos mensajes así:



Como pueden ver, la ultima vez que fue utilizada la cuenta fue en el año 2020, por lo tanto hoy en día está eliminada, pero sirve de ejemplo.
Y así se les enviará repetidamente todas las credenciales.

En fin, espero que les haya gustado la beta del código, como dije, solo lo adapté para hacerlo funcional con Disc. Para la futura versión haré uno 100% mio.
Gracias por leer y probar la funcionalidad del mismo, espero sus sugerencias en los comentarios, gracias.


★゜・。。・゜゜・。。・゜☆゜・。。・゜゜・。。・゜★


He leído el codigo. Por lo que veo lo has hecho en Python, me gustaría ver el mismo en C/C++ pero esta bien la idea. Te faltaria añadirle algun metodo de persistencia entre otras cosas pero sigue asi
#8
Hacking / Re:Hackeando solo con la ip
Abril 29, 2023, 07:37:37 AM
No tienes permitido ver enlaces. Registrate o Entra a tu cuenta
No tienes permitido ver enlaces. Registrate o Entra a tu cuentaHackeando solo con la ip

Cosas necesarias:

1.- Dameware Mini remote control (www.dameware.com)
2.- Una IP


Una vez tenemos instalado el programa, y tenemos la ip de nuestra victima, tendremos que tener en cuenta una cosa:

Que nuestra victima use Windows XP/2000/NT  No tienes permitido ver enlaces. Registrate o Entra a tu cuenta

Y que tenga el usuario administrador sin password.. si nos paramos a pensar... quien tiene el administrador con password? usuarios avanzados... y quien lo tiene sin pass? usuarios normales... a quien atacamos? usuarios normales... jajaj

Bien colocamos la ip en "HOST:" y dejamos el sistema de aunteficacion tal como esta si la victima usa XP, sino cambiamos a la otra opcion. Como saber si la victima usa XP? pues pasandole un scanner buscad x ahi.. scaneres d estos simples. Bueno le damos a conectar.... si todo va bien pondra que se ha conectado y preguntara.

El servidor no esta intalado en la maquina remota... Desea instalarlo? le damos a Yes y empezara a instalarle todo remotamente el servidor. y lo ejecutara cuando esto pase, tendremos en nuestra pantalla su escritorio y controlaremos su mouse, teclado... etc.. igual que VNC pero sin tener acceso fisico al PC.

Cuando dijiste XP se me vino a la cabeza el eternal blue jsjsjsjsj No tienes permitido ver enlaces. Registrate o Entra a tu cuenta
#9
No tienes permitido ver enlaces. Registrate o Entra a tu cuentaes para NASM o FASM??
Diria que nasm por el tema de que usa esa sintaxis y cosas como el mdg db
#10
C / C++ / Re:Cifrado cesar con C
Abril 27, 2023, 07:15:17 PM
No tienes permitido ver enlaces. Registrate o Entra a tu cuentaBuenas tardes sean a todos , pues he estado viendo muchos cifrados en internet pero ninguno me convence o de plano para lo que recien se inician en el lenguaje C es complicado de leer ,asi que les traigo el cifrado cesar usando arrays
y punteros ,pueden mejorar el codigo y ponerlo abajo el objetivo de esto es aprender tecnicas

Bueno mucho blablabla ,aqui el code

Código: c
#include <stdio.h>

void caesarCipher(int key, char *keyword,int size);

int main(int argc,char const *argv[]){
        char msg[4] = "hola";
        char *keyword = &msg;
        caesarCipher(3,keyword,4);
        return 0;
}

void caesarCipher(int key ,char *keyword,int size){
        char abc[26] = {'a','b','c','d','e',
                        'f','g','h','i','j',
                        'k','l','m','n','o',
                        'p','q','r','s','t',
                        'u','v','w','x','y',
                        'z'};
    int buffersize = sizeof(abc)/sizeof(abc[0]);
    int z=0;

    for(int i=0;i<size;i++){
        for(int j=0;j<buffersize;j++){
            if(keyword[i] == abc[j]){
              z = j+key;
                  printf("%c",abc[z]);          
              if(z > buffersize){
                  printf("%c",abc[z-buffersize]);

              } 
            } 
        }
    }
}


En lugar de:
Código: text
        char abc[26] = {'a','b','c','d','e',
                        'f','g','h','i','j',
                        'k','l','m','n','o',
                        'p','q','r','s','t',
                        'u','v','w','x','y',
                        'z'};

haz mejor:
Código: c
        char abc[] = "abcdefghijklmnopqrstuvwyz";

En lugar de hacer esto, que es innecesario:
Código: c
char *keyword = &msg;
pasale msg a la funcion directamente, es correcto:
Código: c
caesarCipher(3,msg,4);

En lugar de poner 4, es mejor usar sizeof(msg) para evitar problemas, aunque en este caso no afecta.
Código: text
caesarCipher(3,msg,sizeof(msg));

aunque esto es innecesario, el tener que pasar el tamaño por función, además que solo te sirve si ya conoces el tamaño del string o si esta declaro en el stack y puedes averiguar su tamaño usando sizeof(). El resto de las veces, se usara strlen(), función de la cabecera string.h que te permite obtener el tamaño de un string.
Luego otras cosa que podemos hacer es agregarle un cuarto argumento en la función que sea un array donde se vaya depositando el contenido ya cifrado, por si queremos usarlo a lo largo del programa para guardarlo en un archivo o similar.
Otro detalle a tener en cuenta es ese doble for que te esta costando una cantidad de i * j iteraciones, lo que es algo caro en términos de tiempo.

Aqui te reporto mi solución, me he tomado la molestia de agregar una función que desencripte:
Código: c
#include <stdio.h>
#include <string.h>

#define ASCII_VALUE_a 97
#define ASCII_VALUE_z 122

#define ASCII_VALUE_A 65
#define ASCII_VALUE_Z 90

void caesarCipher(unsigned char key, const char *msg, char *msg_cifrados);
void desCaesarCipher(unsigned char key, const char *msg, char *msg_descifrados);

int main(int argc, char const *argv[])
{
    char msg[] = "hHoIlLaA";
    char msg_cifrados[sizeof(msg)];

    caesarCipher(2, msg, msg_cifrados);
    printf("\ndatos cifrados almacenados en msg_cifrados: %s", msg_cifrados);

    char msg_descifrados[sizeof(msg)];

    desCaesarCipher(2, msg_cifrados, msg_descifrados);
    printf("\ndatos descifrados en msg_descifrados: %s", msg_descifrados);

    return 0;
}

void caesarCipher(unsigned char key, const char *msg, char *msg_cifrados)
{

    for (unsigned long int i = 0; i < strlen(msg); i++){

        if (msg[i] >= ASCII_VALUE_a && msg[i] <= ASCII_VALUE_z) {
            // la letra es minuscula
            msg_cifrados[i] = msg[i] + key % (ASCII_VALUE_z - ASCII_VALUE_a) ;
        } else if (msg[i] >= ASCII_VALUE_A && msg[i] <= ASCII_VALUE_Z){
            // la letra es mayuscula
            msg_cifrados[i] = msg[i] + key % (ASCII_VALUE_Z - ASCII_VALUE_A);
        }

    }

}
void desCaesarCipher(unsigned char key, const char *msg, char *msg_descifrados)
{

    for (unsigned long int i = 0; i < strlen(msg); i++){

        if (msg[i] >= ASCII_VALUE_a && msg[i] <= ASCII_VALUE_z) {
            // la letra es minuscula
            msg_descifrados[i] = msg[i] - key % (ASCII_VALUE_z - ASCII_VALUE_a) ;
        } else if (msg[i] >= ASCII_VALUE_A && msg[i] <= ASCII_VALUE_Z){
            // la letra es mayuscula
            msg_descifrados[i] = msg[i] - key % (ASCII_VALUE_Z - ASCII_VALUE_A);
        }

    }

}


Para quitarnos bucles extras de encima, aprovechamos que sepamos el valor ascii de los caracters 'a'(97) y 'z'(122), con esto podemos saber si realizamos la resta que 122-97 = 25letras minúsculas.
También sabemos que el rango para las letras mayúsculas abarca de 65('A') a 90('Z').
Lo que haremos sera recorrer los datos cifrados o sin descifrar, depende de cual de las dos funciones hablemos y realizaremos estas restas anteriormente explicadas, tras identificar si la "i" letra de msg es mayúscula o minúscula. Asi podemos determinar si usar el rango de letras mayúsculas o minúsculas. Una vez hecho esto, hacemos la operación modulo a key, con esto nos aseguramos de que, sea el valor que sea key, siempre se encuentre entre un valor que podamos usar para hacer el desplazamiento de diccionario. Una vez tenemos una clave que sepamos que esta entre los limites del rango minúscula o mayúscula, se lo sumamos(encriptar) a la "i" letra de msg o se lo restamos(desencriptar). también mencionar que hemos usado const char por que msg no va a ser un valor a modificar a lo largo de la función. hemos puesto que la key sea sin signo, por que si queremos un desplazamiento negativo, solo habra que exceder el doble del valor limite del rango de valores ASCII de las letras mayúsculas o minúsculas. también agregue que los cambios se guarden en otras variables del mismo tamaño que msg, ya que el tamaño de los datos cifrados y sin descifrar es el mismo también.