ashtar 1.0.10
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 +146 -0
- package/cli/guard.cjs +144 -0
- package/desktop/dist/daemon.cjs +20696 -0
- package/desktop/dist/overlay.html +283 -0
- package/desktop/dist/preload.cjs +7 -0
- package/desktop/resources/.gitkeep +1 -0
- package/desktop/resources/capture_testpad.ps1 +125 -0
- package/desktop/resources/find_testpad.ps1 +37 -0
- package/desktop/resources/inject_dll.ps1 +43 -0
- package/desktop/resources/unprotect.dll +0 -0
- package/dist/cli/index.js +657 -0
- package/dist/shared/index.cjs +553 -0
- package/dist/shared/index.d.cts +47 -0
- package/dist/shared/index.d.ts +47 -0
- package/dist/shared/index.js +518 -0
- package/docs/deployment.md +27 -0
- package/package.json +80 -0
- package/shared/keys.json +6 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title></title>
|
|
7
|
+
<style>
|
|
8
|
+
:root { color-scheme: dark; }
|
|
9
|
+
html, body {
|
|
10
|
+
margin: 0;
|
|
11
|
+
padding: 0;
|
|
12
|
+
background: transparent;
|
|
13
|
+
height: 100%;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
font: 12px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
16
|
+
-webkit-app-region: no-drag;
|
|
17
|
+
-webkit-user-select: text;
|
|
18
|
+
user-select: text;
|
|
19
|
+
pointer-events: none; /* Clicks pass through to apps behind */
|
|
20
|
+
}
|
|
21
|
+
#root {
|
|
22
|
+
position: fixed;
|
|
23
|
+
inset: 0;
|
|
24
|
+
padding: 32px 10px 6px 10px;
|
|
25
|
+
max-height: 100%;
|
|
26
|
+
box-sizing: border-box;
|
|
27
|
+
color: rgba(255, 255, 255, 0.22);
|
|
28
|
+
background: transparent;
|
|
29
|
+
border: none;
|
|
30
|
+
border-radius: 0;
|
|
31
|
+
backdrop-filter: none;
|
|
32
|
+
text-shadow: 0 0 1px rgba(0, 0, 0, 0.30);
|
|
33
|
+
white-space: pre-wrap;
|
|
34
|
+
word-break: break-word;
|
|
35
|
+
overflow-y: auto;
|
|
36
|
+
overflow-x: hidden;
|
|
37
|
+
/* Completely hide scrollbar */
|
|
38
|
+
scrollbar-width: none; /* Firefox */
|
|
39
|
+
-ms-overflow-style: none; /* IE/Edge */
|
|
40
|
+
cursor: text;
|
|
41
|
+
pointer-events: auto; /* Text area captures scroll events */
|
|
42
|
+
}
|
|
43
|
+
/* Hide scrollbar for Chromium / Electron */
|
|
44
|
+
#root::-webkit-scrollbar {
|
|
45
|
+
display: none;
|
|
46
|
+
width: 0;
|
|
47
|
+
height: 0;
|
|
48
|
+
}
|
|
49
|
+
#root.following {
|
|
50
|
+
cursor: grab;
|
|
51
|
+
outline: 1px dashed rgba(255, 255, 255, 0.10);
|
|
52
|
+
outline-offset: 2px;
|
|
53
|
+
}
|
|
54
|
+
@media (prefers-color-scheme: light) {
|
|
55
|
+
#root {
|
|
56
|
+
color: rgba(0, 0, 0, 0.12);
|
|
57
|
+
text-shadow: 0 0 1px rgba(255, 255, 255, 0.25);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
#status {
|
|
61
|
+
position: fixed;
|
|
62
|
+
left: 8px;
|
|
63
|
+
bottom: 4px;
|
|
64
|
+
font-size: 10px;
|
|
65
|
+
color: rgba(255, 255, 255, 0.25);
|
|
66
|
+
text-shadow: 0 0 1px rgba(0, 0, 0, 0.3);
|
|
67
|
+
pointer-events: none;
|
|
68
|
+
transition: opacity 0.3s ease;
|
|
69
|
+
}
|
|
70
|
+
@media (prefers-color-scheme: light) {
|
|
71
|
+
#status {
|
|
72
|
+
color: rgba(0, 0, 0, 0.25);
|
|
73
|
+
text-shadow: 0 0 1px rgba(255, 255, 255, 0.3);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/* Brief "Copied!" flash feedback */
|
|
77
|
+
#copy-toast {
|
|
78
|
+
position: fixed;
|
|
79
|
+
top: 50%;
|
|
80
|
+
left: 50%;
|
|
81
|
+
transform: translate(-50%, -50%) scale(0.9);
|
|
82
|
+
background: rgba(255, 255, 255, 0.08);
|
|
83
|
+
color: rgba(255, 255, 255, 0.35);
|
|
84
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
85
|
+
border-radius: 6px;
|
|
86
|
+
padding: 4px 14px;
|
|
87
|
+
font-size: 11px;
|
|
88
|
+
letter-spacing: 0.5px;
|
|
89
|
+
pointer-events: none;
|
|
90
|
+
opacity: 0;
|
|
91
|
+
transition: opacity 0.2s ease, transform 0.2s ease;
|
|
92
|
+
}
|
|
93
|
+
#copy-toast.show {
|
|
94
|
+
opacity: 1;
|
|
95
|
+
transform: translate(-50%, -50%) scale(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ── Question input (always visible at top) ── */
|
|
99
|
+
#input-bar {
|
|
100
|
+
display: flex;
|
|
101
|
+
position: fixed;
|
|
102
|
+
top: 6px;
|
|
103
|
+
left: 6px;
|
|
104
|
+
right: 6px;
|
|
105
|
+
z-index: 100;
|
|
106
|
+
pointer-events: auto; /* Input bar is always clickable */
|
|
107
|
+
}
|
|
108
|
+
/* input-bar is always visible via display: flex above */
|
|
109
|
+
#question-input {
|
|
110
|
+
flex: 1;
|
|
111
|
+
background: rgba(255, 255, 255, 0.06);
|
|
112
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
113
|
+
border-radius: 4px;
|
|
114
|
+
color: rgba(255, 255, 255, 0.22);
|
|
115
|
+
font: 11px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
116
|
+
padding: 5px 8px;
|
|
117
|
+
outline: none;
|
|
118
|
+
caret-color: rgba(255, 255, 255, 0.30);
|
|
119
|
+
}
|
|
120
|
+
#question-input::placeholder {
|
|
121
|
+
color: rgba(255, 255, 255, 0.15);
|
|
122
|
+
}
|
|
123
|
+
#question-input:focus {
|
|
124
|
+
border-color: rgba(255, 255, 255, 0.15);
|
|
125
|
+
background: rgba(255, 255, 255, 0.08);
|
|
126
|
+
}
|
|
127
|
+
@media (prefers-color-scheme: light) {
|
|
128
|
+
#question-input {
|
|
129
|
+
background: rgba(0, 0, 0, 0.04);
|
|
130
|
+
border-color: rgba(0, 0, 0, 0.1);
|
|
131
|
+
color: rgba(0, 0, 0, 0.5);
|
|
132
|
+
caret-color: rgba(0, 0, 0, 0.3);
|
|
133
|
+
}
|
|
134
|
+
#question-input::placeholder { color: rgba(0, 0, 0, 0.15); }
|
|
135
|
+
#question-input:focus {
|
|
136
|
+
border-color: rgba(0, 0, 0, 0.2);
|
|
137
|
+
background: rgba(0, 0, 0, 0.06);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
</style>
|
|
141
|
+
</head>
|
|
142
|
+
<body>
|
|
143
|
+
<div id="input-bar">
|
|
144
|
+
<input id="question-input" type="text" placeholder="Type question & press Enter..." autocomplete="off" spellcheck="false" />
|
|
145
|
+
</div>
|
|
146
|
+
<div id="root"></div>
|
|
147
|
+
<div id="status"></div>
|
|
148
|
+
<div id="copy-toast">Copied</div>
|
|
149
|
+
<script>
|
|
150
|
+
const { ipcRenderer } = require("electron");
|
|
151
|
+
const root = document.getElementById("root");
|
|
152
|
+
const status = document.getElementById("status");
|
|
153
|
+
const copyToast = document.getElementById("copy-toast");
|
|
154
|
+
const inputBar = document.getElementById("input-bar");
|
|
155
|
+
const questionInput = document.getElementById("question-input");
|
|
156
|
+
let statusTimer = null;
|
|
157
|
+
let copyToastTimer = null;
|
|
158
|
+
|
|
159
|
+
/* ── Receive text from daemon ── */
|
|
160
|
+
ipcRenderer.on("overlay:setText", (_e, text) => {
|
|
161
|
+
if (statusTimer) { clearTimeout(statusTimer); statusTimer = null; }
|
|
162
|
+
root.textContent = text || "";
|
|
163
|
+
root.scrollTop = 0;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
ipcRenderer.on("overlay:appendStatus", (_e, text) => {
|
|
167
|
+
status.textContent = text || "";
|
|
168
|
+
if (statusTimer) clearTimeout(statusTimer);
|
|
169
|
+
statusTimer = setTimeout(() => { status.textContent = ""; statusTimer = null; }, 1400);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
ipcRenderer.on("overlay:setFollowing", (_e, on) => {
|
|
173
|
+
root.classList.toggle("following", !!on);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
ipcRenderer.on("overlay:scrollBy", (_e, delta) => {
|
|
177
|
+
root.scrollTop += delta;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
/* ── Question input mode (for protected apps) ── */
|
|
181
|
+
ipcRenderer.on("overlay:showInput", () => {
|
|
182
|
+
inputBar.classList.add("visible");
|
|
183
|
+
questionInput.value = "";
|
|
184
|
+
questionInput.focus();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
ipcRenderer.on("overlay:hideInput", () => {
|
|
188
|
+
inputBar.classList.remove("visible");
|
|
189
|
+
questionInput.value = "";
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// When user clicks the input, request focus from main process
|
|
193
|
+
questionInput.addEventListener("mousedown", (e) => {
|
|
194
|
+
e.stopPropagation();
|
|
195
|
+
ipcRenderer.send("overlay:requestFocus");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Toggle click-through when mouse enters/leaves input area
|
|
199
|
+
inputBar.addEventListener("mouseenter", () => {
|
|
200
|
+
ipcRenderer.send("overlay:enableMouse");
|
|
201
|
+
});
|
|
202
|
+
inputBar.addEventListener("mouseleave", () => {
|
|
203
|
+
// Only restore click-through if input is not focused
|
|
204
|
+
if (document.activeElement !== questionInput) {
|
|
205
|
+
ipcRenderer.send("overlay:disableMouse");
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
questionInput.addEventListener("keydown", (e) => {
|
|
210
|
+
if (e.key === "Enter") {
|
|
211
|
+
e.preventDefault();
|
|
212
|
+
const q = questionInput.value.trim();
|
|
213
|
+
if (q) {
|
|
214
|
+
ipcRenderer.send("overlay:questionSubmitted", q);
|
|
215
|
+
questionInput.value = "";
|
|
216
|
+
questionInput.blur();
|
|
217
|
+
ipcRenderer.send("overlay:disableMouse");
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (e.key === "Escape") {
|
|
221
|
+
questionInput.value = "";
|
|
222
|
+
questionInput.blur();
|
|
223
|
+
ipcRenderer.send("overlay:inputCancelled");
|
|
224
|
+
ipcRenderer.send("overlay:disableMouse");
|
|
225
|
+
}
|
|
226
|
+
// Stop propagation so hotkeys don't trigger
|
|
227
|
+
e.stopPropagation();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
/* ── Copy text with Ctrl+C / Cmd+C ── */
|
|
231
|
+
document.addEventListener("keydown", (e) => {
|
|
232
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "c") {
|
|
233
|
+
e.preventDefault();
|
|
234
|
+
const text = root.textContent || "";
|
|
235
|
+
if (!text) return;
|
|
236
|
+
|
|
237
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
238
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
239
|
+
showCopyToast();
|
|
240
|
+
}).catch(() => {
|
|
241
|
+
fallbackCopy(text);
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
fallbackCopy(text);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
function fallbackCopy(text) {
|
|
250
|
+
const ta = document.createElement("textarea");
|
|
251
|
+
ta.value = text;
|
|
252
|
+
ta.style.position = "fixed";
|
|
253
|
+
ta.style.left = "-9999px";
|
|
254
|
+
document.body.appendChild(ta);
|
|
255
|
+
ta.select();
|
|
256
|
+
try {
|
|
257
|
+
document.execCommand("copy");
|
|
258
|
+
showCopyToast();
|
|
259
|
+
} catch (err) {
|
|
260
|
+
// silent
|
|
261
|
+
}
|
|
262
|
+
document.body.removeChild(ta);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function showCopyToast() {
|
|
266
|
+
if (copyToastTimer) clearTimeout(copyToastTimer);
|
|
267
|
+
copyToast.classList.add("show");
|
|
268
|
+
copyToastTimer = setTimeout(() => {
|
|
269
|
+
copyToast.classList.remove("show");
|
|
270
|
+
copyToastTimer = null;
|
|
271
|
+
}, 800);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* ── Scroll via mouse wheel even when window is unfocused ── */
|
|
275
|
+
document.addEventListener("wheel", (e) => {
|
|
276
|
+
e.preventDefault();
|
|
277
|
+
root.scrollTop += e.deltaY;
|
|
278
|
+
}, { passive: false });
|
|
279
|
+
|
|
280
|
+
root.addEventListener("scroll", () => {}, { passive: true });
|
|
281
|
+
</script>
|
|
282
|
+
</body>
|
|
283
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# capture_testpad.ps1 — Capture TestPad window using PrintWindow(PW_RENDERFULLCONTENT)
|
|
2
|
+
# This bypasses SetWindowDisplayAffinity completely.
|
|
3
|
+
param([Parameter(Mandatory=$true)][string]$OutputPath)
|
|
4
|
+
|
|
5
|
+
Add-Type -AssemblyName System.Drawing
|
|
6
|
+
Add-Type @"
|
|
7
|
+
using System;
|
|
8
|
+
using System.Runtime.InteropServices;
|
|
9
|
+
using System.Text;
|
|
10
|
+
using System.Drawing;
|
|
11
|
+
using System.Drawing.Imaging;
|
|
12
|
+
|
|
13
|
+
public class SecureCap {
|
|
14
|
+
public delegate bool EnumWindowsProc(IntPtr h, IntPtr l);
|
|
15
|
+
public delegate bool EnumChildProc(IntPtr h, IntPtr l);
|
|
16
|
+
|
|
17
|
+
[DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc cb, IntPtr l);
|
|
18
|
+
[DllImport("user32.dll")] public static extern bool EnumChildWindows(IntPtr parent, EnumChildProc cb, IntPtr l);
|
|
19
|
+
[DllImport("user32.dll")] public static extern int GetWindowText(IntPtr h, StringBuilder s, int m);
|
|
20
|
+
[DllImport("user32.dll")] public static extern int GetClassName(IntPtr h, StringBuilder s, int m);
|
|
21
|
+
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr h);
|
|
22
|
+
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r);
|
|
23
|
+
[DllImport("user32.dll")] public static extern bool GetClientRect(IntPtr h, out RECT r);
|
|
24
|
+
[DllImport("user32.dll")] public static extern bool PrintWindow(IntPtr h, IntPtr hdc, uint flags);
|
|
25
|
+
[DllImport("user32.dll")] public static extern IntPtr GetDC(IntPtr h);
|
|
26
|
+
[DllImport("user32.dll")] public static extern int ReleaseDC(IntPtr h, IntPtr hdc);
|
|
27
|
+
[DllImport("gdi32.dll")] public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
|
|
28
|
+
[DllImport("gdi32.dll")] public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int w, int h);
|
|
29
|
+
[DllImport("gdi32.dll")] public static extern IntPtr SelectObject(IntPtr hdc, IntPtr obj);
|
|
30
|
+
[DllImport("gdi32.dll")] public static extern bool DeleteObject(IntPtr obj);
|
|
31
|
+
[DllImport("gdi32.dll")] public static extern bool DeleteDC(IntPtr hdc);
|
|
32
|
+
|
|
33
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
34
|
+
public struct RECT { public int Left, Top, Right, Bottom; }
|
|
35
|
+
|
|
36
|
+
// Find TestPad main window
|
|
37
|
+
public static IntPtr FindTestpadWindow() {
|
|
38
|
+
IntPtr found = IntPtr.Zero;
|
|
39
|
+
EnumWindows((h, l) => {
|
|
40
|
+
if (!IsWindowVisible(h)) return true;
|
|
41
|
+
var sb = new StringBuilder(512);
|
|
42
|
+
GetWindowText(h, sb, 512);
|
|
43
|
+
string title = sb.ToString().ToLower();
|
|
44
|
+
if (title.Contains("testpad") || title.Contains("chitkara") || title.Contains("exam") || title.Contains("assessment")) {
|
|
45
|
+
found = h;
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
}, IntPtr.Zero);
|
|
50
|
+
return found;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Find the Chrome_WidgetWin_1 child (where webview content renders)
|
|
54
|
+
public static IntPtr FindWebViewChild(IntPtr parent) {
|
|
55
|
+
IntPtr found = IntPtr.Zero;
|
|
56
|
+
EnumChildWindows(parent, (h, l) => {
|
|
57
|
+
var cls = new StringBuilder(256);
|
|
58
|
+
GetClassName(h, cls, 256);
|
|
59
|
+
string cn = cls.ToString();
|
|
60
|
+
if (cn == "Chrome_WidgetWin_1" || cn == "Chrome_RenderWidgetHostHWND" ||
|
|
61
|
+
cn.Contains("WebView") || cn.Contains("Chromium")) {
|
|
62
|
+
RECT cr; GetClientRect(h, out cr);
|
|
63
|
+
if (cr.Right - cr.Left > 200 && cr.Bottom - cr.Top > 200) {
|
|
64
|
+
found = h;
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}, IntPtr.Zero);
|
|
70
|
+
return found;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public static bool Capture(string outputPath) {
|
|
74
|
+
IntPtr mainWnd = FindTestpadWindow();
|
|
75
|
+
if (mainWnd == IntPtr.Zero) return false;
|
|
76
|
+
|
|
77
|
+
// Try to find the actual webview child window
|
|
78
|
+
IntPtr target = FindWebViewChild(mainWnd);
|
|
79
|
+
if (target == IntPtr.Zero) target = mainWnd;
|
|
80
|
+
|
|
81
|
+
// Get window dimensions
|
|
82
|
+
RECT rc;
|
|
83
|
+
GetClientRect(target, out rc);
|
|
84
|
+
int w = rc.Right - rc.Left;
|
|
85
|
+
int h = rc.Bottom - rc.Top;
|
|
86
|
+
if (w <= 0 || h <= 0) {
|
|
87
|
+
GetWindowRect(target, out rc);
|
|
88
|
+
w = rc.Right - rc.Left;
|
|
89
|
+
h = rc.Bottom - rc.Top;
|
|
90
|
+
}
|
|
91
|
+
if (w <= 100 || h <= 100) return false;
|
|
92
|
+
|
|
93
|
+
// Create compatible DC and bitmap
|
|
94
|
+
IntPtr hdcWin = GetDC(target);
|
|
95
|
+
IntPtr hdcMem = CreateCompatibleDC(hdcWin);
|
|
96
|
+
IntPtr hBmp = CreateCompatibleBitmap(hdcWin, w, h);
|
|
97
|
+
IntPtr hOld = SelectObject(hdcMem, hBmp);
|
|
98
|
+
|
|
99
|
+
// PW_RENDERFULLCONTENT = 0x02 — forces hardware-accelerated content to render
|
|
100
|
+
bool ok = PrintWindow(target, hdcMem, 0x02);
|
|
101
|
+
if (!ok) {
|
|
102
|
+
// Fallback without PW_RENDERFULLCONTENT
|
|
103
|
+
ok = PrintWindow(target, hdcMem, 0);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Save bitmap
|
|
107
|
+
if (ok) {
|
|
108
|
+
using (var bmp = Bitmap.FromHbitmap(hBmp)) {
|
|
109
|
+
bmp.Save(outputPath, ImageFormat.Png);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Cleanup
|
|
114
|
+
SelectObject(hdcMem, hOld);
|
|
115
|
+
DeleteObject(hBmp);
|
|
116
|
+
DeleteDC(hdcMem);
|
|
117
|
+
ReleaseDC(target, hdcWin);
|
|
118
|
+
|
|
119
|
+
return ok;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
"@
|
|
123
|
+
|
|
124
|
+
$result = [SecureCap]::Capture($OutputPath)
|
|
125
|
+
Write-Output $result
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# find_testpad.ps1 — Find ALL TestPad PIDs and their display affinity
|
|
2
|
+
# Returns one line per protected window: pid,affinity
|
|
3
|
+
Add-Type @"
|
|
4
|
+
using System;
|
|
5
|
+
using System.Runtime.InteropServices;
|
|
6
|
+
using System.Text;
|
|
7
|
+
using System.Collections.Generic;
|
|
8
|
+
public class TPFind {
|
|
9
|
+
public delegate bool EWP(IntPtr h, IntPtr l);
|
|
10
|
+
[DllImport("user32.dll")] public static extern bool EnumWindows(EWP cb, IntPtr l);
|
|
11
|
+
[DllImport("user32.dll")] public static extern int GetWindowText(IntPtr h, StringBuilder s, int m);
|
|
12
|
+
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr h, out uint p);
|
|
13
|
+
[DllImport("user32.dll")] public static extern bool GetWindowDisplayAffinity(IntPtr h, out uint a);
|
|
14
|
+
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr h);
|
|
15
|
+
public static string FindAll() {
|
|
16
|
+
var seen = new HashSet<uint>();
|
|
17
|
+
var results = new List<string>();
|
|
18
|
+
EnumWindows((h, l) => {
|
|
19
|
+
if (!IsWindowVisible(h)) return true;
|
|
20
|
+
var t = new StringBuilder(512);
|
|
21
|
+
GetWindowText(h, t, 512);
|
|
22
|
+
string s = t.ToString().ToLower();
|
|
23
|
+
if (s.Contains("testpad") || s.Contains("chitkara") || s.Contains("exam") || s.Contains("assessment")) {
|
|
24
|
+
uint pid; GetWindowThreadProcessId(h, out pid);
|
|
25
|
+
if (!seen.Contains(pid)) {
|
|
26
|
+
uint aff; GetWindowDisplayAffinity(h, out aff);
|
|
27
|
+
results.Add(pid + "," + aff);
|
|
28
|
+
seen.Add(pid);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}, IntPtr.Zero);
|
|
33
|
+
return string.Join("\n", results);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
"@
|
|
37
|
+
[TPFind]::FindAll()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[Parameter(Mandatory=$true)][int]$TargetPid,
|
|
3
|
+
[Parameter(Mandatory=$true)][string]$DllPath
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
Add-Type @"
|
|
7
|
+
using System;
|
|
8
|
+
using System.Runtime.InteropServices;
|
|
9
|
+
using System.Text;
|
|
10
|
+
public class DllInject {
|
|
11
|
+
[DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(uint a, bool b, int pid);
|
|
12
|
+
[DllImport("kernel32.dll")] public static extern IntPtr VirtualAllocEx(IntPtr h, IntPtr a, uint s, uint t, uint p);
|
|
13
|
+
[DllImport("kernel32.dll")] public static extern bool WriteProcessMemory(IntPtr h, IntPtr b, byte[] buf, uint s, out int w);
|
|
14
|
+
[DllImport("kernel32.dll")] public static extern IntPtr GetModuleHandle(string n);
|
|
15
|
+
[DllImport("kernel32.dll")] public static extern IntPtr GetProcAddress(IntPtr h, string n);
|
|
16
|
+
[DllImport("kernel32.dll")] public static extern IntPtr CreateRemoteThread(IntPtr h, IntPtr a, uint s, IntPtr start, IntPtr param, uint f, out uint tid);
|
|
17
|
+
[DllImport("kernel32.dll")] public static extern uint WaitForSingleObject(IntPtr h, uint ms);
|
|
18
|
+
[DllImport("kernel32.dll")] public static extern bool VirtualFreeEx(IntPtr h, IntPtr a, uint s, uint t);
|
|
19
|
+
[DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr h);
|
|
20
|
+
public static bool Inject(int pid, string dllPath) {
|
|
21
|
+
IntPtr hProc = OpenProcess(0x1F0FFF, false, pid);
|
|
22
|
+
if (hProc == IntPtr.Zero) return false;
|
|
23
|
+
byte[] pathBytes = Encoding.Unicode.GetBytes(dllPath + "\0");
|
|
24
|
+
IntPtr mem = VirtualAllocEx(hProc, IntPtr.Zero, (uint)pathBytes.Length, 0x3000, 0x40);
|
|
25
|
+
if (mem == IntPtr.Zero) { CloseHandle(hProc); return false; }
|
|
26
|
+
int written;
|
|
27
|
+
WriteProcessMemory(hProc, mem, pathBytes, (uint)pathBytes.Length, out written);
|
|
28
|
+
IntPtr kernel32 = GetModuleHandle("kernel32.dll");
|
|
29
|
+
IntPtr loadLib = GetProcAddress(kernel32, "LoadLibraryW");
|
|
30
|
+
uint tid;
|
|
31
|
+
IntPtr hThread = CreateRemoteThread(hProc, IntPtr.Zero, 0, loadLib, mem, 0, out tid);
|
|
32
|
+
if (hThread == IntPtr.Zero) { VirtualFreeEx(hProc, mem, 0, 0x8000); CloseHandle(hProc); return false; }
|
|
33
|
+
WaitForSingleObject(hThread, 10000);
|
|
34
|
+
VirtualFreeEx(hProc, mem, 0, 0x8000);
|
|
35
|
+
CloseHandle(hThread);
|
|
36
|
+
CloseHandle(hProc);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
"@
|
|
41
|
+
|
|
42
|
+
$result = [DllInject]::Inject($TargetPid, $DllPath)
|
|
43
|
+
Write-Output $result
|
|
Binary file
|