Created
January 28, 2026 14:45
-
-
Save justaguywhocodes/c0b12ed5d16dd59446c90a9832e59aa1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // testdll.c | |
| // Simple Windows DLL that exports a function compatible with rundll32.exe | |
| // When called via rundll32.exe testdll.dll,ExecuteTest | |
| // it writes "test executed" to C:\Users\Public\Downloads\test.txt | |
| #include <windows.h> | |
| #include <stdio.h> | |
| // Exported function (rundll32-compatible signature) | |
| extern "C" __declspec(dllexport) void CALLBACK ExecuteTest( | |
| HWND hwnd, // unused | |
| HINSTANCE hinst, // unused | |
| LPSTR lpszCmdLine, // unused | |
| int nCmdShow // unused | |
| ) | |
| { | |
| FILE* file = NULL; | |
| errno_t err = fopen_s(&file, "C:\\Users\\Public\\Downloads\\test.txt", "w"); | |
| if (err == 0 && file != NULL) | |
| { | |
| fprintf(file, "test executed"); | |
| fclose(file); | |
| } | |
| // If fopen_s fails, file remains NULL and nothing is written (silent failure, same as original) | |
| } | |
| // Required DllMain (minimal implementation) | |
| BOOL APIENTRY DllMain( | |
| HMODULE hModule, | |
| DWORD ul_reason_for_call, | |
| LPVOID lpReserved | |
| ) | |
| { | |
| switch (ul_reason_for_call) | |
| { | |
| case DLL_PROCESS_ATTACH: | |
| case DLL_THREAD_ATTACH: | |
| case DLL_THREAD_DETACH: | |
| case DLL_PROCESS_DETACH: | |
| break; | |
| } | |
| return TRUE; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "pch.h" | |
| #include <windows.h> | |
| #include <tchar.h> | |
| #include <strsafe.h> | |
| // DLL entry point | |
| BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { | |
| switch (ul_reason_for_call) { | |
| case DLL_PROCESS_ATTACH: | |
| case DLL_THREAD_ATTACH: | |
| case DLL_THREAD_DETACH: | |
| case DLL_PROCESS_DETACH: | |
| break; | |
| } | |
| return TRUE; | |
| } | |
| // Exported function for regsvr32 compliance | |
| EXTERN_C __declspec(dllexport) HRESULT STDAPICALLTYPE DllRegisterServer(void) { | |
| // For testing purposes, this installs a sample service that loads a DLL (e.g., core.dll) | |
| // Note: This requires administrative privileges to run successfully. | |
| // In a real scenario, adjust paths and ensure the target DLL exists. | |
| // This is a simulation of the TTP; use in isolated environments only. | |
| SC_HANDLE schSCManager = NULL; | |
| SC_HANDLE schService = NULL; | |
| HRESULT hr = S_OK; | |
| // Open the service control manager | |
| schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); | |
| if (schSCManager == NULL) { | |
| hr = HRESULT_FROM_WIN32(GetLastError()); | |
| goto Cleanup; | |
| } | |
| // Define the service parameters | |
| // Assuming core.dll is in C:\Test\ and has an export named "Run" (adjust as needed) | |
| // The binPath uses rundll32.exe to load and execute the DLL's export | |
| TCHAR szBinPath[MAX_PATH]; | |
| StringCchPrintf(szBinPath, MAX_PATH, _T("C:\\Windows\\System32\\rundll32.exe C:\\temp\\core.dll,ExecuteTest")); | |
| // Create the service | |
| schService = CreateService( | |
| schSCManager, // SCM database | |
| _T("TestStagerService"), // Service name (change for uniqueness) | |
| _T("Test Stager Service"), // Display name | |
| SERVICE_ALL_ACCESS, // Desired access | |
| SERVICE_WIN32_OWN_PROCESS, // Service typec | |
| SERVICE_AUTO_START, // Start type | |
| SERVICE_ERROR_NORMAL, // Error control | |
| szBinPath, // Path to service binary | |
| NULL, // No load ordering group | |
| NULL, // No tag identification | |
| NULL, // No dependencies | |
| NULL, // LocalSystem account | |
| NULL // No password | |
| ); | |
| if (schService == NULL) { | |
| DWORD err = GetLastError(); | |
| if (err != ERROR_SERVICE_EXISTS) { // Ignore if service already exists | |
| hr = HRESULT_FROM_WIN32(err); | |
| goto Cleanup; | |
| } | |
| } | |
| // Optionally start the service (comment out if not needed) | |
| // StartService(schService, 0, NULL); | |
| Cleanup: | |
| if (schService != NULL) { | |
| CloseServiceHandle(schService); | |
| } | |
| if (schSCManager != NULL) { | |
| CloseServiceHandle(schSCManager); | |
| } | |
| return hr; | |
| } | |
| // Optional: DllUnregisterServer to remove the service (for completeness) | |
| EXTERN_C __declspec(dllexport) HRESULT STDAPICALLTYPE DllUnregisterServer(void) { | |
| SC_HANDLE schSCManager = NULL; | |
| SC_HANDLE schService = NULL; | |
| HRESULT hr = S_OK; | |
| schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); | |
| if (schSCManager == NULL) { | |
| hr = HRESULT_FROM_WIN32(GetLastError()); | |
| goto Cleanup; | |
| } | |
| schService = OpenService(schSCManager, _T("TestStagerService"), DELETE | SERVICE_STOP); | |
| if (schService == NULL) { | |
| hr = HRESULT_FROM_WIN32(GetLastError()); | |
| goto Cleanup; | |
| } | |
| // Stop the service if running | |
| SERVICE_STATUS_PROCESS ssp; | |
| DWORD dwBytesNeeded; | |
| QueryServiceStatusEx(schService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded); | |
| if (ssp.dwCurrentState != SERVICE_STOPPED) { | |
| ControlService(schService, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp); | |
| } | |
| // Delete the service | |
| if (!DeleteService(schService)) { | |
| hr = HRESULT_FROM_WIN32(GetLastError()); | |
| } | |
| Cleanup: | |
| if (schService != NULL) { | |
| CloseServiceHandle(schService); | |
| } | |
| if (schSCManager != NULL) { | |
| CloseServiceHandle(schSCManager); | |
| } | |
| return hr; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment