framework for LRSS

This commit is contained in:
xwashere 2024-03-05 17:46:15 -05:00
parent e0b69fe182
commit 96619b66eb
Signed by: XWasHere
GPG Key ID: 042F8BFA1B0EF93B
11 changed files with 424 additions and 45 deletions

View File

@ -93,6 +93,46 @@ function(lw_add_executable NAME)
set_property(SOURCE TARGET_DIRECTORY ${NAME} APPEND PROPERTY OBJECT_DEPENDS set_property(SOURCE TARGET_DIRECTORY ${NAME} APPEND PROPERTY OBJECT_DEPENDS
GCC-install GCC-install
) )
target_include_directories(${NAME} PUBLIC "${CMAKE_SOURCE_DIR}/common/include")
else()
message(FATAL_ERROR "LW_TARGET not defined, are you in a lw_project?")
endif()
endfunction()
# defines a library target. this must be used if lw_project is used
function(lw_add_library NAME)
set(cpa_flag SHARED)
set(cpa_single)
set(cpa_multi SOURCES)
cmake_parse_arguments(PARSE_ARGV 1 ARG "${cpa_flag}" "${cpa_single}" "${cpa_multi}")
if (ARG_SHARED)
add_library(${NAME} SHARED)
else()
add_library(${NAME})
endif()
if(ARG_SOURCES)
target_sources(${NAME} PRIVATE ${ARG_SOURCES})
target_compile_options(${NAME} PRIVATE
$<$<COMPILE_LANGUAGE:C>:${LW_CROSS_CFLAGS}>
$<$<COMPILE_LANGUAGE:CXX>:${LW_CROSS_CXXFLAGS}>
)
target_link_options(${NAME} PRIVATE ${LW_CROSS_LDFLAGS})
endif()
if(LW_TARGET)
set_property(TARGET ${NAME} APPEND PROPERTY LINK_DEPENDS
$<TARGET_FILE:LindowsLinkerScript>
$<TARGET_FILE:LindowsCompilerSpec>
)
set_property(SOURCE TARGET_DIRECTORY ${NAME} APPEND PROPERTY OBJECT_DEPENDS
GCC-install
)
target_include_directories(${NAME} PUBLIC "${CMAKE_SOURCE_DIR}/common/include")
else() else()
message(FATAL_ERROR "LW_TARGET not defined, are you in a lw_project?") message(FATAL_ERROR "LW_TARGET not defined, are you in a lw_project?")
endif() endif()
@ -133,6 +173,7 @@ ExternalProject_Add(GCC
--disable-bootstrap --disable-bootstrap
BUILD_COMMAND make BUILD_COMMAND make
INSTALL_COMMAND make install INSTALL_COMMAND make install
COMMAND cp -r ${CMAKE_BINARY_DIR}/gcc/prefix/lib64 ${CMAKE_BINARY_DIR}/gcc/lib64
STEP_TARGETS install STEP_TARGETS install
) )
@ -267,7 +308,7 @@ add_custom_command(OUTPUT lindows_c.img
if=/dev/zero if=/dev/zero
of=lindows_c.img of=lindows_c.img
bs=1M bs=1M
count=256 count=512
# format disk with new NTFS filesystem # format disk with new NTFS filesystem
COMMAND "${NTFS3g_Mkfs_EXECUTABLE}" ARGS -F # scary! COMMAND "${NTFS3g_Mkfs_EXECUTABLE}" ARGS -F # scary!
-L Windows -L Windows
@ -303,8 +344,13 @@ add_custom_command(OUTPUT lindows_c.img
# copy curses to system32 # copy curses to system32
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/scripts/copylib ncurses COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/scripts/copylib ncurses
# copy lsmss # continuedd
COMMAND ${CoreUtils_Copy_EXECUTABLE} ARGS $<TARGET_FILE:lsmss> lindows_c/Windows/System32/lsmss.exe COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/scripts/copylib gcc lib64
# copy lindows binaries
COMMAND ${CoreUtils_Copy_EXECUTABLE} ARGS $<TARGET_FILE:lsmss> lindows_c/Windows/System32/lsmss.exe
COMMAND ${CoreUtils_Copy_EXECUTABLE} ARGS $<TARGET_FILE:lrss> lindows_c/Windows/System32/lrss.exe
COMMAND ${CoreUtils_Copy_EXECUTABLE} ARGS $<TARGET_FILE:lrssclient> lindows_c/Windows/System32/liblrssclient.so
# create users folder # create users folder
COMMAND ${CoreUtils_Mkdir_EXECUTABLE} ARGS lindows_c/Users COMMAND ${CoreUtils_Mkdir_EXECUTABLE} ARGS lindows_c/Users
@ -313,7 +359,7 @@ add_custom_command(OUTPUT lindows_c.img
COMMAND "umount" ARGS lindows_c COMMAND "umount" ARGS lindows_c
DEPENDS DEPENDS
lsmss lsmss lrss lrssclient
Linux-install Linux-install
GLibC-install GLibC-install
Bash-install Bash-install
@ -351,12 +397,12 @@ add_custom_command(OUTPUT lindows.img
COMMAND echo ARGS label: gpt > disk.sfdisk COMMAND echo ARGS label: gpt > disk.sfdisk
COMMAND echo ARGS unit: sectors >> disk.sfdisk COMMAND echo ARGS unit: sectors >> disk.sfdisk
COMMAND echo ARGS start=1MiB, size=33MiB, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B >> disk.sfdisk COMMAND echo ARGS start=1MiB, size=33MiB, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B >> disk.sfdisk
COMMAND echo ARGS start=34MiB, size=256MiB, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, uuid=48D29DA8-2FF8-4F23-BA1A-0E8CCFC329E2 >> disk.sfdisk COMMAND echo ARGS start=34MiB, size=512MiB, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, uuid=48D29DA8-2FF8-4F23-BA1A-0E8CCFC329E2 >> disk.sfdisk
COMMAND "${CoreUtils_dd_EXECUTABLE}" ARGS COMMAND "${CoreUtils_dd_EXECUTABLE}" ARGS
if=/dev/zero if=/dev/zero
of=lindows.img of=lindows.img
bs=1M bs=1M
count=300 count=600
# partition disk # partition disk
COMMAND sfdisk ARGS lindows.img < disk.sfdisk COMMAND sfdisk ARGS lindows.img < disk.sfdisk
# write partitions # write partitions
@ -371,7 +417,7 @@ add_custom_command(OUTPUT lindows.img
if=lindows_c.img if=lindows_c.img
of=lindows.img of=lindows.img
bs=1M bs=1M
count=256 count=512
seek=34 seek=34
conv=notrunc conv=notrunc
DEPENDS lindows_c.img lindows_efi.img DEPENDS lindows_c.img lindows_efi.img
@ -381,3 +427,4 @@ add_custom_command(OUTPUT lindows.img
add_custom_target(lindows ALL DEPENDS lindows.img) add_custom_target(lindows ALL DEPENDS lindows.img)
add_subdirectory(lsmss) add_subdirectory(lsmss)
add_subdirectory(lrss)

4
README
View File

@ -1,3 +1,7 @@
== NAMING == == NAMING ==
Pii: Preinit internal core routines Pii: Preinit internal core routines
Lrc: Lindows Registry client
== SUBSYSTEM IPC ==
When a subsystem starts, LSMSS will wait for it to send a SIGSTOP to itself, afterwards, it will send a continue signal then keep going.

View File

@ -0,0 +1,11 @@
#pragma once
#include <stdint.h>
/**
* Windows NT Error code
*/
typedef uint32_t NTSTATUS;
#define STATUS_SUCCESS 0x00000000
#define STATUS_INTERNAL_ERROR 0xC00000E5

16
lrss/CMakeLists.txt Normal file
View File

@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.28)
lw_project(lrss
DESCRIPTION "Lindows Registry Subsystem"
TARGET LINDOWS
)
lw_add_executable(lrss
SOURCES main.cxx
)
target_link_libraries(lrss PRIVATE lrssclient)
lw_add_library(lrssclient SHARED
SOURCES lib/library.cxx
)
target_include_directories(lrssclient PUBLIC include)

2
lrss/README Normal file
View File

@ -0,0 +1,2 @@
== Lindows Registry Subsystem ==
The lindows registry subsystem preforms all operations related to the windows registry, when a linux program runs, it connects to LRSS, authenticates and then communicates to it via a unix domain socket, when a windows program runs, it connects to the wineserver, which is patched to use LRSS instead of it's builtin registry code.

View File

@ -0,0 +1,27 @@
#pragma once
#include <lindows/ntstatus.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Get the address of the LRSS socket
* \param buffer (out) buffer to write the address to
* \return status code:
* STATUS_SUCCESS: I really hope this function always succeeds
*/
extern NTSTATUS LrcSocketAddress(char Buffer[108]);
/**
* Open a connection to LRSS
* \return status code
* STATUS_SUCCESS: Registry connection has been established
* STATUS_INTERNAL_ERROR: Something went wrong
*/
extern NTSTATUS LrcOpenRegistry();
#ifdef __cplusplus
}
#endif

63
lrss/lib/library.cxx Normal file
View File

@ -0,0 +1,63 @@
#include <cstring>
#include <cstdio>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <lindows/lrss.h>
static const char SOCKET_ADDRESS[108] = {
0, 'L', 'R', 'S', 'S', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};
static int REGISTRY_SOCKET = -1;
NTSTATUS LrcSocketAddress(char Buffer[108]) {
std::memcpy(Buffer, SOCKET_ADDRESS, 108);
return STATUS_SUCCESS;
}
NTSTATUS LrcOpenRegistry() {
// This may not work if the thing got closed somehow :>
if (REGISTRY_SOCKET == -1) {
REGISTRY_SOCKET = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (REGISTRY_SOCKET < 0) {
REGISTRY_SOCKET = -1;
return STATUS_INTERNAL_ERROR;
}
int tmp = 1;
if (setsockopt(REGISTRY_SOCKET, SOL_SOCKET, SO_PASSCRED, &tmp, sizeof(tmp)) < 0) {
close(REGISTRY_SOCKET);
REGISTRY_SOCKET = -1;
return STATUS_INTERNAL_ERROR;
}
struct sockaddr_un sockaddr {
.sun_family = AF_UNIX,
.sun_path = {}
};
LrcSocketAddress(sockaddr.sun_path);
if (connect(REGISTRY_SOCKET, reinterpret_cast<struct sockaddr*>(&sockaddr), sizeof(sockaddr)) < 0) {
close(REGISTRY_SOCKET);
REGISTRY_SOCKET = -1;
return STATUS_INTERNAL_ERROR;
}
return STATUS_SUCCESS;
} else return STATUS_SUCCESS;
}

103
lrss/main.cxx Normal file
View File

@ -0,0 +1,103 @@
#include <iostream>
#include <vector>
#include <unordered_map>
#include <sys/socket.h>
#include <sys/un.h>
#include <poll.h>
#include <signal.h>
#include <lindows/lrss.h>
class ConnectionState {
// -- general --
int socket;
public:
// -- constructors --
ConnectionState(int sock) : socket(sock) {};
};
int main() {
int tmp;
// Hi
std::cout << "Starting Lindows Registry Subsystem\n";
// Set up handshake socket (used to be used for a handshake, kept the name, sry)
struct sockaddr_un theSockAddr {
.sun_family = AF_UNIX,
.sun_path = {}
};
// Get the listening address
if (auto err = LrcSocketAddress(theSockAddr.sun_path)) {
std::cerr << "Failed to get LRSS socket address: " << err << std::endl;
return 1;
}
// Create the socket
int handshakeSocket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (handshakeSocket < 0) {
perror("socket");
return 1;
}
// Configure the socket
tmp = 1;
if (setsockopt(handshakeSocket, SOL_SOCKET, SO_PASSCRED, &tmp, sizeof(tmp)) < 0) {
perror("setsockopt");
return 1;
}
// Bind to the socket
if (bind(handshakeSocket, reinterpret_cast<sockaddr*>(&theSockAddr), sizeof(theSockAddr)) < 0) {
perror("bind");
return 1;
}
// Listen on da socket
if (listen(handshakeSocket, 20) < 0) {
perror("listen");
return 1;
}
// Wait for LSMSS
raise(SIGSTOP);
// Main loop
std::unordered_map<int, ConnectionState> connections {};
std::vector<pollfd> pollfds {{ handshakeSocket, POLLIN, 0 }};
while (true) {
// Poll for whateversies
if (poll(pollfds.data(), pollfds.size(), -1) < 0) {
perror("poll");
return 1;
}
// First fd is our handshake socket
if (pollfds[0].revents) {
if (pollfds[0].revents & POLLIN) {
// Accept the connection and add it to our client list
int sock = accept(handshakeSocket, nullptr, nullptr);
if (sock < 0) {
perror("accept");
}
connections.emplace(sock, sock);
pollfds.emplace(pollfds.end(), sock, POLLIN | POLLHUP, 0);
}
pollfds[0].revents = 0;
}
// Iterate over client sockets
for (auto it = ++pollfds.begin(); it < pollfds.end(); it++) {
std::cout << it->fd << " " << it->revents << "\n";
it->revents = 0;
}
}
return 1;
}

View File

@ -9,5 +9,5 @@ lw_project(lsmss
lw_add_executable(lsmss lw_add_executable(lsmss
SOURCES main.cxx SOURCES main.cxx
) )
target_link_options(lsmss PRIVATE "-static") target_link_options(lsmss PRIVATE "-static")
target_include_directories(lsmss PRIVATE $<TARGET_PROPERTY:lrssclient,INCLUDE_DIRECTORIES>)

View File

@ -3,19 +3,100 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <filesystem> #include <filesystem>
#include <vector>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <dlfcn.h>
#include <lindows/lrss.h>
void PiiLog(const std::string& message);
[[noreturn]] void PiiAbortBoot();
/**
i* State for a subsystem managed by LSMSS
*/
class PiiSubsystemState {
// -- General info --
std::string SubsystemName; // Subsystem display name in debug logs
bool Running = false; // Subsystem is running
// -- Linux information --
pid_t LnxPid = 0; // PID
std::filesystem::path LnxPath; // Binary path
public:
// -- Constructors --
PiiSubsystemState(const std::string& name, const std::filesystem::path& path) {
this->SubsystemName = name;
this->LnxPath = path;
return;
}
// -- General --
/**
* Start the subsystem process
*/
void Start() {
PiiLog("Starting subsystem " + this->SubsystemName);
if (pid_t child = fork()) {
// save the process id
this->LnxPid = child;
this->Running = true;
while (true) {
int status;
if (waitpid(this->LnxPid, &status, WUNTRACED) < 0) {
perror("waitpid");
PiiLog("Failed to start " + this->SubsystemName);
this->Running = false;
return;
}
if (WIFEXITED(status)) {
PiiLog("Failed to start " + this->SubsystemName + " (Exit code " + std::to_string(WEXITSTATUS(status)) + ")");
this->Running = false;
return;
}
if (WIFSTOPPED(status)) {
if (kill(this->LnxPid, SIGCONT) < 0) {
perror("kill");
PiiLog("Failed to start " + this->SubsystemName + ". Unknown state, boot process cannot continue.");
PiiAbortBoot();
}
PiiLog("Started subsystem " + this->SubsystemName);
return;
}
}
return;
} else {
char* args[] = { strdup(this->LnxPath.c_str()), nullptr };
execv(args[0], args);
// this should never happen
std::exit(1);
}
}
};
/** /**
* Log something. * Log something.
* *
* \param message to log * \param message to log
*/ */
void Pii_Log(const std::string& message) { void PiiLog(const std::string& message) {
std::cout << message << "\n"; std::cout << message << "\n";
return; return;
} }
@ -26,7 +107,7 @@ void Pii_Log(const std::string& message) {
* \param path absolute path to program (duh) * \param path absolute path to program (duh)
* \return program return value, 1 if it does not exist or -1 on error * \return program return value, 1 if it does not exist or -1 on error
*/ */
int Pii_ExecuteProgram(const std::string& path) { int PiiExecuteProgram(const std::string& path) {
if (pid_t child = fork()) { if (pid_t child = fork()) {
int exitcode = -1; int exitcode = -1;
waitpid(child, &exitcode, 0); waitpid(child, &exitcode, 0);
@ -45,36 +126,53 @@ int Pii_ExecuteProgram(const std::string& path) {
* Abort the boot process and drop the user into a recovery shell * Abort the boot process and drop the user into a recovery shell
*/ */
[[noreturn]] [[noreturn]]
void Pii_AbortBoot() { void PiiAbortBoot() {
Pii_Log("Unable to complete preinitialization. You have been dropped into a minimal recovery environment. Good luck."); PiiLog("Unable to complete preinitialization. You have been dropped into a minimal recovery environment. Good luck.");
Pii_ExecuteProgram("/Windows/System32/bash.exe"); PiiExecuteProgram("/Windows/System32/bash.exe");
std::exit(1); std::exit(1);
} }
int main() { int main() {
std::vector<PiiSubsystemState> subsystems {};
// -- just print shit -- // -- just print shit --
Pii_Log("Starting Lindows Session Manager Subsystem"); PiiLog("Starting Lindows Session Manager Subsystem");
// -- dynamic linking fun -- // -- dynamic linking fun --
Pii_Log("Regenerating dynamic linker cache"); PiiLog("Regenerating dynamic linker cache");
Pii_ExecuteProgram("/Windows/System32/ldconfig.exe"); PiiExecuteProgram("/Windows/System32/ldconfig.exe");
// -- registry -- // -- registry --
Pii_Log("Starting Lindows Registry Subsystem"); PiiLog("Starting Lindows Registry Subsystem");
// TODO subsystems.emplace(subsystems.end(), "Lindows Registry Subsystem", "/Windows/System32/lrss.exe")
Pii_Log("Loading Lindows registry"); ->Start(); // We don't actually know if this started, we aren't in the mainloop. We just have to pray :>
PiiLog("Loading Lindows registry");
auto liblrssclient = dlopen("/Windows/System32/liblrssclient.so", RTLD_NOW);
auto pLrcOpenRegistry = reinterpret_cast<decltype(LrcOpenRegistry)*>(dlsym(liblrssclient, "LrcOpenRegistry"));
if (auto error = dlerror()) {
PiiLog(error);
PiiAbortBoot();
}
if (pLrcOpenRegistry()) {
PiiLog("Failed to connect to registry subsystem.");
PiiAbortBoot();
}
// TODO // TODO
// -- swap -- // -- swap --
Pii_Log("Creating pagefiles"); PiiLog("Creating pagefiles");
// TODO // TODO
// -- env -- // -- env --
Pii_Log("Setting environment variables"); PiiLog("Setting environment variables");
// TODO // TODO
// we are never ok // we are never ok
Pii_AbortBoot(); PiiAbortBoot();
return 0; return 0;
} }

View File

@ -1,30 +1,38 @@
#!/bin/sh #!/bin/sh
# ARG 1: LIB PATH # ARG 1: LIB PATH
# ARG 2: LIB DIR
# copy libs main() {
find $1/bin $1/lib -type d | sed -r 's/'$1'\/(bin|lib)\///;Tn;s/^/mkdir lindows_c\/Windows\/System32\//;by;:n;d;:y;e' > /dev/null; # copy libs
find $1/bin -type f | sed -r 's/'$1'\/bin\/(.*)/cp '$1'\/bin\/\1 lindows_c\/Windows\/System32\/\1.exe/;e' > /dev/null find $1/bin $1/$2 -type d | sed -r 's/'$1'\/(bin|'$2')\///;Tn;s/^/mkdir lindows_c\/Windows\/System32\//;by;:n;d;:y;e' > /dev/null;
find $1/lib -type f | sed -r 's/'$1'\/lib\/(.*)/cp '$1'\/lib\/\1 lindows_c\/Windows\/System32\/\1/;e' > /dev/null find $1/bin -type f | sed -r 's/'$1'\/bin\/(.*)/cp '$1'\/bin\/\1 lindows_c\/Windows\/System32\/\1.exe/;e' > /dev/null
find $1/$2 -type f | sed -r 's/'$1'\/'$2'\/(.*)/cp '$1'\/'$2'\/\1 lindows_c\/Windows\/System32\/\1/;e' > /dev/null
# ntfs-3g cant seem to figure out ntfs symlinks, its 1500 lines of code for reparse points dont even work # ntfs-3g cant seem to figure out ntfs symlinks, its 1500 lines of code for reparse points dont even work
# with symlinks, so we have to build the reparse point and do it ourselves # with symlinks, so we have to build the reparse point and do it ourselves
for lib in $(cd $1/lib && find -type l); do for lib in $(cd $1/$2 && find -type l); do
WPATH="$(echo -n $(readlink "$1/lib/$lib"))" WPATH="$(echo -n $(readlink "$1/$2/$lib"))"
LPATH="$(echo -n '/Windows/System32/'"$WPATH" | sed -E 's/(.)/\1\\x00/g;s/\//\\\\/g')" # sed -E 's/(.)/\\0\1/g'); LPATH="$(echo -n '/Windows/System32/'"$WPATH" | sed -E 's/(.)/\1\\x00/g;s/\//\\\\/g')" # sed -E 's/(.)/\\0\1/g');
LPATHL=$(( 36 + ${#WPATH} * 2 )); LPATHL=$(( 36 + ${#WPATH} * 2 ));
LPATHL1=$(printf "%o" $((($LPATHL >> 8 ) & 255 )) ); LPATHL1=$(printf "%o" $((($LPATHL >> 8 ) & 255 )) );
LPATHL0=$(printf "%o" $((($LPATHL ) & 255 )) ); LPATHL0=$(printf "%o" $((($LPATHL ) & 255 )) );
LBUF="\\x00\\x00\\$LPATHL0\\$LPATHL1\\x00\\x00\\$LPATHL0\\$LPATHL1\\x00\\x00\\x00\\x00$LPATH" LBUF="\\x00\\x00\\$LPATHL0\\$LPATHL1\\x00\\x00\\$LPATHL0\\$LPATHL1\\x00\\x00\\x00\\x00$LPATH"
LBUFL=$(( 12 + $LPATHL )) LBUFL=$(( 12 + $LPATHL ))
LBUFL1=$(printf "%o" $((($LBUFL >> 8) & 255 )) ); LBUFL1=$(printf "%o" $((($LBUFL >> 8) & 255 )) );
LBUFL0=$(printf "%o" $((($LBUFL ) & 255 )) ); LBUFL0=$(printf "%o" $((($LBUFL ) & 255 )) );
touch "lindows_c/Windows/System32/$lib"; touch "lindows_c/Windows/System32/$lib";
setfattr -h -n system.ntfs_reparse_data \ setfattr -h -n system.ntfs_reparse_data \
-v "0S$(printf "\x0C\\x00\\x00\xA0\\$LBUFL0\\$LBUFL1\\x00\\x00$LBUF" | base64 -w0)"\ -v "0S$(printf "\x0C\\x00\\x00\xA0\\$LBUFL0\\$LBUFL1\\x00\\x00$LBUF" | base64 -w0)"\
"lindows_c/Windows/System32/$lib"; "lindows_c/Windows/System32/$lib";
#ln -sr "lindows_c/Windows/System32/$(readlink $1/lib/$lib)" "lindows_c/Windows/System32/$lib" #ln -sr "lindows_c/Windows/System32/$(readlink $1/lib/$lib)" "lindows_c/Windows/System32/$lib"
done done
}
[ -z "$2" ] && main "$1" lib;
[ -n "$2" ] && main "$1" "$2";
exit 0;