Starting some threads

This commit is contained in:
ptitSeb 2018-12-24 18:24:43 +01:00
parent bef89e80e1
commit f1bd98e603
20 changed files with 306 additions and 34 deletions

View File

@ -37,6 +37,7 @@ SET(ELFLOADER_SRC
src/elfload_dump.c
src/librarian.c
src/stack.c
src/threads.c
src/wrapper.c
src/x86emu.c
src/x86run.c
@ -50,7 +51,7 @@ SET(ELFLOADER_SRC
)
add_executable(box86 ${ELFLOADER_SRC})
target_link_libraries(box86 m dl rt)
target_link_libraries(box86 m dl rt pthread)
add_test(test01 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/box86
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test01 -D TEST_OUTPUT=tmpfile.txt
@ -75,4 +76,9 @@ add_test(test04 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/box86
add_test(test05 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/box86
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test05 -D TEST_ARGS2=7 -D TEST_OUTPUT=tmpfile.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref05.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test06 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/box86
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test06 -D TEST_OUTPUT=tmpfile.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref06.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake )

View File

@ -8,6 +8,7 @@ typedef struct elfheader_s elfheader_t;
typedef struct x86emu_s x86emu_t;
typedef struct zydis_s zydis_t;
typedef struct lib_s lib_t;
typedef struct bridge_s bridge_t;
typedef struct box86context_s {
path_collection_t box86_path; // PATH env. variable
@ -36,6 +37,8 @@ typedef struct box86context_s {
lib_t *maplib; // lib and symbols handling
bridge_t *threads; // threads
} box86context_t;
box86context_t *NewBox86Context(int argc);

View File

@ -3,18 +3,9 @@
#include <stdint.h>
#include "bridge.h"
#include "bridge_private.h"
#include "wrapper.h"
#pragma pack(push, 1)
typedef struct onebridge_s {
uint8_t CC; // CC int 0x3
uint8_t S, C; // 'S' 'C', just a signature
wrapper_t w; // wrapper
uintptr_t f; // the function for the wrapper
uint8_t C3; // C3 ret
} onebridge_t;
#pragma pack(pop)
#define NBRICK 16
typedef struct brick_s brick_t;
typedef struct brick_s {

17
src/bridge_private.h Executable file
View File

@ -0,0 +1,17 @@
#ifndef __BRIDGE_PRIVATE_H_
#define __BRIDGE_PRIVATE_H_
#include <stdint.h>
#include "wrapper.h"
#pragma pack(push, 1)
typedef struct onebridge_s {
uint8_t CC; // CC int 0x3
uint8_t S, C; // 'S' 'C', just a signature
wrapper_t w; // wrapper
uintptr_t f; // the function for the wrapper
uint8_t C3; // C3 ret
} onebridge_t;
#pragma pack(pop)
#endif //__BRIDGE_PRIVATE_H_

View File

@ -301,8 +301,9 @@ void AddGlobalsSymbols(lib_t* maplib, elfheader_t* h)
if(((h->SymTab[i].st_info == 18) || h->SymTab[i].st_info == 17) && (h->SymTab[i].st_other==0) && (h->SymTab[i].st_shndx!=0)) {
const char * symname = h->StrTab+h->SymTab[i].st_name;
uintptr_t offs = h->SymTab[i].st_value + h->delta;
printf_log(LOG_DUMP, "Adding Symbol \"%s\" with offset=%p\n", symname, offs);
AddSymbol(maplib, symname, offs);
uint32_t sz = h->SymTab[i].st_size;
printf_log(LOG_DUMP, "Adding Symbol \"%s\" with offset=%p sz=%d\n", symname, offs, sz);
AddSymbol(maplib, symname, offs, sz);
}
}
}

View File

@ -1,6 +1,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "debug.h"
#include "librarian.h"
#include "librarian_private.h"
@ -39,11 +41,13 @@ int32_t my__libc_start_main(x86emu_t* emu, int *(main) (int, char * *, char * *)
int argc, char * * ubp_av, void (*init) (void), void (*fini) (void),
void (*rtld_fini) (void), void (* stack_end)); // implemented in x86run_private.c
uint32_t LibSyscall(x86emu_t *emu); // implemented in x86syscall.c
int my_pthread_create(x86emu_t *emu, void* t, void* attr, void* start_routine, void* arg); //implemented in thread.c
uintptr_t CreateSymbol(lib_t *maplib, const char* name)
{
// look for symbols that can be created
uintptr_t addr = 0;
// libc
if(strcmp(name, "__stack_chk_fail")==0) {
addr = AddBridge(maplib->bridge, vFE, my__stack_chk_fail);
} else if(strcmp(name, "__libc_start_main")==0) {
@ -64,17 +68,30 @@ uintptr_t CreateSymbol(lib_t *maplib, const char* name)
addr = AddBridge(maplib->bridge, iFi, putchar);
} else if(strcmp(name, "strtol")==0) {
addr = AddBridge(maplib->bridge, iFppi, strtol);
} else if(strcmp(name, "strerror")==0) {
addr = AddBridge(maplib->bridge, pFv, strerror);
} //pthread
else if(strcmp(name, "pthread_self")==0) {
addr = AddBridge(maplib->bridge, uFv, pthread_self);
} else if(strcmp(name, "pthread_create")==0) {
addr = AddBridge(maplib->bridge, iFEpppp, my_pthread_create);
} else if(strcmp(name, "pthread_equal")==0) {
addr = AddBridge(maplib->bridge, iFuu, pthread_equal);
} else if(strcmp(name, "pthread_join")==0) {
addr = AddBridge(maplib->bridge, iFup, pthread_join);
}
if(addr)
AddSymbol(maplib, name, addr);
AddSymbol(maplib, name, addr, 12);
return addr;
}
void AddSymbol(lib_t *maplib, const char* name, uintptr_t addr)
void AddSymbol(lib_t *maplib, const char* name, uintptr_t addr, uint32_t sz)
{
int ret;
khint_t k = kh_put(maplib_t, maplib->maplib, name, &ret);
kh_value(maplib->maplib, k).offs = addr;
kh_value(maplib->maplib, k).sz = sz;
}
uintptr_t FindSymbol(lib_t *maplib, const char* name)
{
@ -83,3 +100,13 @@ uintptr_t FindSymbol(lib_t *maplib, const char* name)
return CreateSymbol(maplib, name);
return kh_val(maplib->maplib, k).offs;
}
int GetSymbolStartEnd(lib_t* maplib, const char* name, uintptr_t* start, uintptr_t* end)
{
khint_t k = kh_get(maplib_t, maplib->maplib, name);
if(k==kh_end(maplib->maplib))
return 0;
*start = kh_val(maplib->maplib, k).offs;
*end = *start + kh_val(maplib->maplib, k).sz;
return 1;
}

View File

@ -8,7 +8,8 @@ typedef struct bridge_s bridge_t;
lib_t *NewLibrarian();
void FreeLibrarian(lib_t **maplib);
void AddSymbol(lib_t *maplib, const char* name, uintptr_t addr);
void AddSymbol(lib_t *maplib, const char* name, uintptr_t addr, uint32_t sz);
uintptr_t FindSymbol(lib_t *maplib, const char* name);
int GetSymbolStartEnd(lib_t* maplib, const char* name, uintptr_t* start, uintptr_t* end);
#endif //__LIBRARIAN_H_

View File

@ -5,6 +5,7 @@
typedef struct {
uintptr_t offs;
uint32_t sz;
// need to track type of symbol?
// need to track origin?
} onelib_t;

View File

@ -129,7 +129,7 @@ int main(int argc, const char **argv, const char **env) {
// init random seed
srandom(time(NULL));
// check BOX86_loG debug level
// check BOX86_LOG debug level
LoadLogEnv();
// Create a new context
@ -153,8 +153,7 @@ int main(int argc, const char **argv, const char **env) {
p = getenv("BOX86_TRACE");
if(p) {
setbuf(stdout, NULL);
if (strcmp(p, "1")==0)
if (strcmp(p, "0"))
context->x86trace = 1;
}
if(context->x86trace) {
@ -243,9 +242,20 @@ int main(int argc, const char **argv, const char **env) {
// setup the stack...
Push(context->emu, (uint32_t)context->argv);
Push(context->emu, context->argc);
SetupX86Emu(context->emu);
SetupX86Emu(context->emu, NULL, NULL);
SetEAX(context->emu, context->argc);
SetEBX(context->emu, (uint32_t)context->argv);
p = getenv("BOX86_TRACE");
if(p) {
setbuf(stdout, NULL);
uintptr_t trace_start, trace_end;
if (strcmp(p, "1")==0)
SetTraceEmu(context->emu, 0, 0);
else if (GetSymbolStartEnd(context->maplib, p, &trace_start, &trace_end))
SetTraceEmu(context->emu, trace_start, trace_end);
}
// emulate!
printf_log(LOG_DEBUG, "Start x86emu on Main\n");
Run(context->emu);

57
src/threads.c Executable file
View File

@ -0,0 +1,57 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "box86context.h"
#include "threads.h"
#include "x86emu_private.h"
#include "bridge_private.h"
#include "x86run.h"
#include "x86emu.h"
#include "stack.h"
// memory handling to be perfected...
// keep a hash thread_t -> emu to set emu->quit to 1 on pthread_cancel
typedef struct emuthread_s {
x86emu_t *emu;
onebridge_t routine;
int stacksize;
void *stack;
} emuthread_t;
void* pthread_routine(void* p)
{
emuthread_t *et = (emuthread_t*)p;
Run(et->emu);
void* r = (void*)GetEAX(et->emu);
FreeX86Emu(&et->emu);
free(et->stack);
free(et);
return r;
}
int my_pthread_create(x86emu_t *emu, void* t, void* attr, void* start_routine, void* arg)
{
emuthread_t *emuthread = (emuthread_t*)calloc(1, sizeof(emuthread_t));
emuthread->routine.CC = 0xCC;
emuthread->routine.S = 'S'; emuthread->routine.C = 'C';
emuthread->routine.w = pFp;
emuthread->routine.f = (uintptr_t)start_routine;
emuthread->routine.C3 = 0xC3;
emuthread->stacksize = 2*1024*1024; //default stack size is 2Mo
// TODO: get stack size inside attr
emuthread->stack = calloc(1, emuthread->stacksize);
emuthread->emu = NewX86Emu(emu->context, (uintptr_t)start_routine, (uintptr_t)emuthread->stack,
emuthread->stacksize);
SetupX86Emu(emuthread->emu, emu->shared_global, emu->globals);
emuthread->emu->trace_start = emu->trace_start;
emuthread->emu->trace_end = emu->trace_end;
Push(emuthread->emu, (uintptr_t)arg);
PushExit(emuthread->emu);
// create thread
return pthread_create((pthread_t*)t, (const pthread_attr_t *)attr,
pthread_routine, emuthread);
}

7
src/threads.h Executable file
View File

@ -0,0 +1,7 @@
#ifndef _THREADS_H_
#define _THREADS_H_
#include "bridge.h"
#endif //_THREADS_H_

View File

@ -23,15 +23,23 @@ typedef void (*vFv_t)();
typedef void (*vFp_t)(void*);
typedef void (*vFE_t)(x86emu_t*);
typedef int32_t (*iFv_t)();
typedef uint32_t (*uFv_t)();
typedef void* (*pFv_t)();
typedef uint32_t (*uFE_t)(x86emu_t*);
typedef void (*vFi_t)(int32_t);
typedef int32_t (*iFi_t)(int32_t);
typedef int32_t (*iFp_t)(void*);
typedef void* (*pFp_t)(void*);
typedef int32_t (*iFpp_t)(void*, void*);
typedef int32_t (*iFii_t)(int32_t, int32_t);
typedef int32_t (*iFip_t)(int32_t, void*);
typedef int32_t (*iFuu_t)(uint32_t, uint32_t);
typedef int32_t (*iFup_t)(uint32_t, void*);
typedef int32_t (*iFppi_t)(void*, void*, int32_t);
typedef void* (*pFuu_t)(uint32_t, uint32_t);
typedef int32_t (*iFipp_t)(int, void*, void*);
typedef int32_t (*iFppp_t)(void*, void*, void*);
typedef int32_t (*iFEpppp_t)(x86emu_t*, void*, void*, void*, void*);
typedef int32_t (*iFEpippppp_t)(x86emu_t*, void*, int32_t, void*, void*, void*, void*, void*);
#define DEF(A) A f = (A)fnc
@ -54,7 +62,12 @@ void vFE(x86emu_t *emu, uintptr_t fnc)
void iFv(x86emu_t *emu, uintptr_t fnc)
{
DEF(iFv_t);
*(int32_t*)&R_EAX = f();
R_EAX = *(uint32_t*)f();
}
void uFv(x86emu_t *emu, uintptr_t fnc)
{
DEF(uFv_t);
R_EAX = f();
}
void uFE(x86emu_t *emu, uintptr_t fnc)
{
@ -66,6 +79,11 @@ void vFi(x86emu_t *emu, uintptr_t fnc)
DEF(vFi_t);
f(i32(0));
}
void pFv(x86emu_t *emu, uintptr_t fnc)
{
DEF(pFv_t);
R_EAX = *(uint32_t*)f();
}
void iFi(x86emu_t *emu, uintptr_t fnc)
{
DEF(iFi_t);
@ -76,6 +94,11 @@ void iFp(x86emu_t *emu, uintptr_t fnc)
DEF(iFp_t);
R_EAX = (uint32_t)f(p(0));
}
void pFp(x86emu_t *emu, uintptr_t fnc)
{
DEF(pFp_t);
R_EAX = (uint32_t)f(p(0));
}
void iFpp(x86emu_t *emu, uintptr_t fnc)
{
DEF(iFpp_t);
@ -91,6 +114,26 @@ void pFuu(x86emu_t *emu, uintptr_t fnc)
DEF(pFuu_t);
R_EAX = (uintptr_t)f(u32(0), u32(4));
}
void iFii(x86emu_t *emu, uintptr_t fnc)
{
DEF(iFii_t);
R_EAX = (uint32_t)f(i32(0), i32(4));
}
void iFip(x86emu_t *emu, uintptr_t fnc)
{
DEF(iFip_t);
R_EAX = (uint32_t)f(i32(0), p(4));
}
void iFuu(x86emu_t *emu, uintptr_t fnc)
{
DEF(iFuu_t);
R_EAX = (uint32_t)f(u32(0), u32(4));
}
void iFup(x86emu_t *emu, uintptr_t fnc)
{
DEF(iFup_t);
R_EAX = (uint32_t)f(u32(0), p(4));
}
void iFpV(x86emu_t *emu, uintptr_t fnc)
{
DEF(iFpp_t);
@ -111,6 +154,11 @@ void iFvopV(x86emu_t *emu, uintptr_t fnc)
DEF(iFppp_t);
R_EAX = (uint32_t)f((void*)stdout, p(4), (void*)stack(8));
}
void iFEpppp(x86emu_t *emu, uintptr_t fnc)
{
DEF(iFEpppp_t);
R_EAX = (uint32_t)f(emu, p(0), p(4), p(8), p(12));
}
void iFEpippppp(x86emu_t *emu, uintptr_t fnc)
{
DEF(iFEpippppp_t);

View File

@ -20,16 +20,24 @@ void vFp(x86emu_t *emu, uintptr_t fnc);
void vFE(x86emu_t *emu, uintptr_t fnc);
void uFE(x86emu_t *emu, uintptr_t fnc);
void iFv(x86emu_t *emu, uintptr_t fnc);
void uFv(x86emu_t *emu, uintptr_t fnc);
void pFv(x86emu_t *emu, uintptr_t fnc);
void vFi(x86emu_t *emu, uintptr_t fnc);
void iFi(x86emu_t *emu, uintptr_t fnc);
void iFp(x86emu_t *emu, uintptr_t fnc);
void pFp(x86emu_t *emu, uintptr_t fnc);
void iFpp(x86emu_t *emu, uintptr_t fnc);
void iFppi(x86emu_t *emu, uintptr_t fnc);
void pFuu(x86emu_t *emu, uintptr_t fnc);
void iFii(x86emu_t *emu, uintptr_t fnc);
void iFip(x86emu_t *emu, uintptr_t fnc);
void iFuu(x86emu_t *emu, uintptr_t fnc);
void iFup(x86emu_t *emu, uintptr_t fnc);
void iFpv(x86emu_t *emu, uintptr_t fnc);
void iF1pV(x86emu_t *emu, uintptr_t fnc);
void iFopV(x86emu_t *emu, uintptr_t fnc);
void iFvopV(x86emu_t *emu, uintptr_t fnc);
void iFEpppp(x86emu_t *emu, uintptr_t fnc);
void iFEpippppp(x86emu_t *emu, uintptr_t fnc); // this is __libc_start_main basically

View File

@ -51,20 +51,34 @@ x86emu_t *NewX86Emu(box86context_t *context, uintptr_t start, uintptr_t stack, i
return emu;
}
void SetupX86Emu(x86emu_t *emu)
void SetupX86Emu(x86emu_t *emu, int* shared_global, void* globals)
{
printf_log(LOG_DEBUG, "Setup X86 Emu\n");
// push "end emu" marker address
PushExit(emu);
// Setup the GS segment:
emu->globals = calloc(1, 256); // arbitrary 256 byte size?
// calc canary...
uint8_t canary[4];
for (int i=0; i<4; ++i) canary[i] = 1 + getrand(255);
canary[getrand(4)] = 0;
memcpy(emu->globals+0x14, canary, sizeof(canary)); // put canary in place
printf_log(LOG_DEBUG, "Setting up canary (for Stack protector) at GS:0x14, value:%08X\n", *(uint32_t*)canary);
if(shared_global) {
emu->globals = globals;
emu->shared_global = shared_global;
} else {
emu->globals = calloc(1, 256); // arbitrary 256 byte size?
// calc canary...
uint8_t canary[4];
for (int i=0; i<4; ++i) canary[i] = 1 + getrand(255);
canary[getrand(4)] = 0;
memcpy(emu->globals+0x14, canary, sizeof(canary)); // put canary in place
printf_log(LOG_DEBUG, "Setting up canary (for Stack protector) at GS:0x14, value:%08X\n", *(uint32_t*)canary);
emu->shared_global = (int*)calloc(1, sizeof(int));
}
(*emu->shared_global)++;
}
void SetTraceEmu(x86emu_t *emu, uintptr_t trace_start, uintptr_t trace_end)
{
printf_log(LOG_INFO, "Setting trace only between %p and %p\n", trace_start, trace_end);
emu->trace_start = trace_start;
emu->trace_end = trace_end;
}
void FreeX86Emu(x86emu_t **x86emu)
@ -74,8 +88,11 @@ void FreeX86Emu(x86emu_t **x86emu)
printf_log(LOG_DEBUG, "Free a X86 Emu (%p)\n", *x86emu);
if((*x86emu)->dec)
DeleteX86TraceDecoder(&(*x86emu)->dec);
if((*x86emu)->globals)
free((*x86emu)->globals);
if((*x86emu)->shared_global && !(*(*x86emu)->shared_global)--) {
if((*x86emu)->globals)
free((*x86emu)->globals);
free((*x86emu)->shared_global);
}
free(*x86emu);
*x86emu = NULL;
}

View File

@ -5,7 +5,8 @@ typedef struct x86emu_s x86emu_t;
typedef struct box86context_s box86context_t;
x86emu_t *NewX86Emu(box86context_t *context, uintptr_t start, uintptr_t stack, int stacksize);
void SetupX86Emu(x86emu_t *emu);
void SetupX86Emu(x86emu_t *emu, int* shared_gloabl, void* globals);
void SetTraceEmu(x86emu_t *emu, uintptr_t trace_start, uintptr_t trace_end);
void FreeX86Emu(x86emu_t **x86emu);
uint32_t GetEAX(x86emu_t *emu);

View File

@ -34,8 +34,10 @@ typedef struct x86emu_s {
int error;
// trace
zydis_dec_t *dec;
uintptr_t trace_start, trace_end;
// global stuffs, pointed with GS: segment
void *globals;
int *shared_global;
// parent context
box86context_t *context;
} x86emu_t;

View File

@ -13,10 +13,13 @@
int Run(x86emu_t *emu)
{
printf_log(LOG_DEBUG, "Run X86, EIP=%p\n", emu, R_EIP);
emu->quit = 0;
while (!emu->quit)
{
if(emu->dec) {
if(emu->dec && (
(emu->trace_end == 0)
|| ((R_EIP >= emu->trace_start) && (R_EIP < emu->trace_end))) ) {
printf_log(LOG_NONE, "%s", DumpCPURegs(emu));
if(Peek(emu, 0)==0xcc && Peek(emu, 1)=='S' && Peek(emu, 2)=='C') {
uint32_t a = *(uint32_t*)(R_EIP+3);
@ -26,7 +29,7 @@ int Run(x86emu_t *emu)
printf_log(LOG_NONE, "%08p: Native call to %p\n", R_EIP, a);
}
} else {
printf_log(LOG_NONE, "%08p: %s\n", R_EIP, DecodeX86Trace(emu->dec, R_EIP));
printf_log(LOG_NONE, "%s\n", DecodeX86Trace(emu->dec, R_EIP));
}
}
uint8_t opcode = Fetch8(emu);
@ -242,6 +245,16 @@ int Run(x86emu_t *emu)
if(!ACCESS_FLAG(F_ZF))
R_EIP += tmp8s;
break;
case 0x76: /* JBE Ib */
tmp8s = Fetch8s(emu);
if((ACCESS_FLAG(F_ZF) || ACCESS_FLAG(F_CF)))
R_EIP += tmp8s;
break;
case 0x77: /* JNBE Ib */
tmp8s = Fetch8s(emu);
if(!(ACCESS_FLAG(F_ZF) || ACCESS_FLAG(F_CF)))
R_EIP += tmp8s;
break;
case 0x7C: /* JL Ib */
tmp8s = Fetch8s(emu);
if(ACCESS_FLAG(F_SF) != ACCESS_FLAG(F_OF))
@ -326,6 +339,9 @@ int Run(x86emu_t *emu)
GetG(emu, &op2, nextop);
op2->dword[0] = (uint32_t)&op1->dword[0];
break;
case 0x90: /* NOP */
break;
case 0xA1: /* MOV EAX, Od */
R_EAX = *(uint32_t*)Fetch32(emu);

4
tests/ref06.txt Executable file
View File

@ -0,0 +1,4 @@
[02] Second thread executing
[02] Thread done.
[00] Done.

BIN
tests/test06 Executable file

Binary file not shown.

55
tests/test06.c Executable file
View File

@ -0,0 +1,55 @@
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
const int thread_count = 2;
pthread_t tid[2];
const char *thread_messages[2] = {
"First thread executing",
"Second thread executing"
};
void *doSomething(void *arg)
{
pthread_t id = pthread_self();
int num = -1;
for (int i = 0 ; i < thread_count ; ++i)
{
if (pthread_equal(id, tid[i]))
{
num = i + 1;
if (num == 2) printf("[%02d] %s\n", num, thread_messages[i]);
break;
}
}
for (unsigned int i = 0 ; i < 0x10000 ; ++i);
if (num == 2) printf("[%02d] Thread done.\n", num);
return NULL;
}
int main(int argc, char const *argv[])
{
int err;
for (int i = 0 ; i < thread_count ; ++i)
{
//printf("[00] Thread %d starting\n", i + 1);
err = pthread_create(&tid[i], NULL, doSomething, NULL);
if (err)
{
printf("[00] Couldn't create thread %d: %s\n", i + 1, strerror(err));
}
for (unsigned int i = 0 ; i < 0x1000 ; ++i);
}
//printf("[00] Waiting for all threads to end...\n");
for (int i = 0 ; i < thread_count ; ++i)
pthread_join(tid[i], NULL);
printf("\n[00] Done.\n");
return 0;
}