1+ #include " Rebuild.h"
2+ #include " Logger.h"
3+ #include < filesystem>
4+ #include < fstream>
5+ #include < random>
6+ #include < thread>
7+ #include < windows.h>
8+ #include < shlwapi.h>
9+
10+ #pragma comment(lib, "shlwapi.lib")
11+
12+ std::string Rebuild::random_suffix_; // Initialize static member
13+
14+ Rebuild::Rebuild () {
15+ // Generate suffix once per run
16+ if (random_suffix_.empty ()) {
17+ random_suffix_ = generateRandomString (8 );
18+ }
19+ }
20+
21+ std::string Rebuild::generateRandomString (size_t length) {
22+ static const char charset[] = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ;
23+ std::random_device rd;
24+ std::mt19937 gen (rd ());
25+ std::uniform_int_distribution<> dis (0 , sizeof (charset) - 2 );
26+ std::string result;
27+ result.reserve (length);
28+ for (size_t i = 0 ; i < length; ++i) {
29+ result += charset[dis (gen)];
30+ }
31+ return result;
32+ }
33+
34+ bool Rebuild::isValidExecutable (const std::string& filePath) {
35+ std::ifstream file (filePath, std::ios::binary);
36+ if (!file) {
37+ Logger::logWarning (std::format (" Cannot open file for validation: {}" , filePath));
38+ return false ;
39+ }
40+
41+ char header[2 ];
42+ file.read (header, 2 );
43+ file.close ();
44+ bool isValid = (header[0 ] == ' M' && header[1 ] == ' Z' );
45+ if (!isValid) {
46+ Logger::logWarning (std::format (" File is not a valid PE executable: {}" , filePath));
47+ }
48+ return isValid;
49+ }
50+
51+ std::string Rebuild::getCurrentExeName () {
52+ char exePath[MAX_PATH];
53+ DWORD pathLength = GetModuleFileNameA (NULL , exePath, MAX_PATH);
54+ if (pathLength == 0 ) {
55+ Logger::logWarning (std::format (" Failed to get module filename: {}" , GetLastError ()));
56+ return " ErScripts.exe" ;
57+ }
58+
59+ std::string fullPath (exePath, pathLength);
60+ size_t lastSlash = fullPath.find_last_of (" \\ /" );
61+ if (lastSlash != std::string::npos) {
62+ return fullPath.substr (lastSlash + 1 );
63+ }
64+ return fullPath;
65+ }
66+
67+ std::string Rebuild::getCurrentExePath () {
68+ char exePath[MAX_PATH];
69+ DWORD pathLength = GetModuleFileNameA (NULL , exePath, MAX_PATH);
70+ if (pathLength == 0 ) {
71+ Logger::logWarning (std::format (" Failed to get module path: {}" , GetLastError ()));
72+ return " ErScripts.exe" ;
73+ }
74+ return std::string (exePath, pathLength);
75+ }
76+
77+ bool Rebuild::unpackIfNeeded () {
78+ std::string current_exe = getCurrentExePath ();
79+
80+ // Read binary
81+ std::ifstream in_file (current_exe, std::ios::binary);
82+ if (!in_file) {
83+ Logger::logWarning (std::format (" Failed to read binary for unpacking: {}" , current_exe));
84+ return false ;
85+ }
86+ std::vector<char > binary_data ((std::istreambuf_iterator<char >(in_file)), std::istreambuf_iterator<char >());
87+ in_file.close ();
88+
89+ // Check for marker (2 bytes: random byte + 0xFF)
90+ if (binary_data.size () < 2 || binary_data[binary_data.size () - 1 ] != 0xFF ) {
91+ return true ; // Not packed
92+ }
93+
94+ // Remove random byte and marker
95+ binary_data.pop_back (); // Marker (0xFF)
96+ binary_data.pop_back (); // Random byte
97+
98+ // Write unpacked binary
99+ for (int retry = 0 ; retry < 3 ; ++retry) {
100+ std::ofstream out_file (current_exe, std::ios::binary);
101+ if (out_file) {
102+ out_file.write (binary_data.data (), binary_data.size ());
103+ out_file.close ();
104+ return true ;
105+ }
106+ Logger::logWarning (std::format (" Retry {}: Failed to write unpacked binary: {}" , retry + 1 , GetLastError ()));
107+ std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
108+ }
109+ Logger::logWarning (" Failed to unpack binary after retries" );
110+ return false ;
111+ }
112+
113+ bool Rebuild::createPolymorphicBinary (std::string& output_path) {
114+ // Get TEMP directory
115+ char temp_dir[MAX_PATH];
116+ if (GetTempPathA (MAX_PATH, temp_dir) == 0 ) {
117+ Logger::logWarning (std::format (" Failed to get TEMP path: {}" , GetLastError ()));
118+ return false ;
119+ }
120+
121+ // Use stored suffix
122+ output_path = std::string (temp_dir) + " ErScripts_temp_" + random_suffix_ + " .exe" ;
123+
124+ // Check disk space (~10MB)
125+ ULARGE_INTEGER freeBytes;
126+ if (GetDiskFreeSpaceExA (temp_dir, &freeBytes, NULL , NULL )) {
127+ if (freeBytes.QuadPart < 10 * 1024 * 1024 ) {
128+ Logger::logWarning (" Insufficient disk space for rebuild" );
129+ return false ;
130+ }
131+ }
132+
133+ // Read current binary
134+ std::string current_exe = getCurrentExePath ();
135+ std::ifstream in_file (current_exe, std::ios::binary);
136+ if (!in_file) {
137+ Logger::logWarning (" Failed to read current binary for rebuild" );
138+ return false ;
139+ }
140+ std::vector<char > binary_data ((std::istreambuf_iterator<char >(in_file)), std::istreambuf_iterator<char >());
141+ in_file.close ();
142+
143+ // Append one random byte
144+ std::random_device rd;
145+ std::mt19937 gen (rd ());
146+ std::uniform_int_distribution<int > byte_dis (0 , 255 );
147+ binary_data.push_back (static_cast <char >(byte_dis (gen)));
148+
149+ // Append marker (0xFF)
150+ binary_data.push_back (static_cast <char >(0xFF ));
151+
152+ // Write temp binary
153+ for (int retry = 0 ; retry < 3 ; ++retry) {
154+ std::ofstream out_file (output_path, std::ios::binary);
155+ if (out_file) {
156+ out_file.write (binary_data.data (), binary_data.size ());
157+ out_file.close ();
158+ return true ;
159+ }
160+ Logger::logWarning (std::format (" Retry {}: Failed to write temp binary: {}" , retry + 1 , GetLastError ()));
161+ std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
162+ }
163+ Logger::logWarning (" Failed to write temp binary after retries" );
164+ return false ;
165+ }
166+
167+ bool Rebuild::replaceWithNewBinary (const std::string& newFilePath, const std::string& bat_path) {
168+ if (!isValidExecutable (newFilePath)) {
169+ Logger::logWarning (" New binary is not a valid executable" );
170+ return false ;
171+ }
172+
173+ std::string current_exe = getCurrentExePath ();
174+
175+ // Write batch file
176+ for (int retry = 0 ; retry < 3 ; ++retry) {
177+ std::ofstream bat_file (bat_path);
178+ if (bat_file) {
179+ bat_file << " @echo off\n " ;
180+ bat_file << " timeout /t 1 >nul\n " ;
181+ bat_file << " move /Y \" " << newFilePath << " \" \" " << current_exe << " \"\n " ;
182+ bat_file << " if exist \" " << current_exe << " \" (\n " ;
183+ bat_file << " start \"\" \" " << current_exe << " \" --run /requestuia\n " ;
184+ bat_file << " del \" %~f0\"\n " ;
185+ bat_file << " )\n " ;
186+ bat_file.close ();
187+ break ;
188+ }
189+ Logger::logWarning (std::format (" Retry {}: Failed to create batch file: {}" , retry + 1 , GetLastError ()));
190+ std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
191+ }
192+
193+ if (!std::filesystem::exists (bat_path)) {
194+ Logger::logWarning (" Failed to create batch file after retries" );
195+ return false ;
196+ }
197+
198+ // Run batch file
199+ SHELLEXECUTEINFOA sei = { sizeof (sei) };
200+ sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE;
201+ sei.lpFile = bat_path.c_str ();
202+ sei.lpVerb = " open" ;
203+ sei.nShow = SW_HIDE;
204+ if (!ShellExecuteExA (&sei)) {
205+ Logger::logWarning (std::format (" Failed to execute batch file: {}" , GetLastError ()));
206+ std::filesystem::remove (bat_path);
207+ return false ;
208+ }
209+
210+ return true ;
211+ }
212+
213+ void Rebuild::cleanupTempFiles () {
214+ char temp_dir[MAX_PATH];
215+ if (GetTempPathA (MAX_PATH, temp_dir) == 0 ) {
216+ Logger::logWarning (std::format (" Failed to get TEMP path for cleanup: {}" , GetLastError ()));
217+ return ;
218+ }
219+
220+ std::string temp_exe = std::string (temp_dir) + " ErScripts_temp_" + random_suffix_ + " .exe" ;
221+ std::string temp_bat = std::string (temp_dir) + " rebuild_" + random_suffix_ + " .bat" ;
222+
223+ if (std::filesystem::exists (temp_exe)) {
224+ std::filesystem::remove (temp_exe);
225+ Logger::logInfo (std::format (" Cleaned up temp file: {}" , temp_exe));
226+ }
227+ if (std::filesystem::exists (temp_bat)) {
228+ std::filesystem::remove (temp_bat);
229+ Logger::logInfo (std::format (" Cleaned up batch file: {}" , temp_bat));
230+ }
231+ }
232+
233+ bool Rebuild::rebuildAndRelaunch (bool should_rebuild) {
234+ if (!should_rebuild) {
235+ // Unpack for --run
236+ return unpackIfNeeded ();
237+ }
238+
239+ // Unpack before rebuild
240+ if (!unpackIfNeeded ()) {
241+ Logger::logWarning (" Failed to unpack binary before rebuild" );
242+ return false ;
243+ }
244+
245+ // Create new binary
246+ std::string new_binary;
247+ if (!createPolymorphicBinary (new_binary)) {
248+ Logger::logWarning (" Failed to create polymorphic binary" );
249+ return false ;
250+ }
251+
252+ // Create batch file in TEMP
253+ char temp_dir[MAX_PATH];
254+ if (GetTempPathA (MAX_PATH, temp_dir) == 0 ) {
255+ Logger::logWarning (std::format (" Failed to get TEMP path: {}" , GetLastError ()));
256+ std::filesystem::remove (new_binary);
257+ return false ;
258+ }
259+ std::string bat_path = std::string (temp_dir) + " rebuild_" + random_suffix_ + " .bat" ;
260+
261+ // Replace and relaunch
262+ if (replaceWithNewBinary (new_binary, bat_path)) {
263+ Logger::logInfo (" Rebuild initiated. Relaunching..." );
264+ return true ;
265+ }
266+ else {
267+ Logger::logWarning (" Failed to replace binary for rebuild" );
268+ std::filesystem::remove (new_binary);
269+ if (std::filesystem::exists (bat_path)) {
270+ std::filesystem::remove (bat_path);
271+ }
272+ return false ;
273+ }
274+ }
0 commit comments