├── .gitattributes
├── .gitignore
├── README.md
├── java_opstack_viewer.rc
├── java_opstack_viewer.sln
├── java_opstack_viewer.vcxproj
├── java_opstack_viewer.vcxproj.filters
├── main.cpp
└── resource.h
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear on external disk
35 | .Spotlight-V100
36 | .Trashes
37 |
38 | # Directories potentially created on remote AFP share
39 | .AppleDB
40 | .AppleDesktop
41 | Network Trash Folder
42 | Temporary Items
43 | .apdisk
44 | *.opensdf
45 | *.tlog
46 | *.pdb
47 | *.res
48 | *.sdf
49 | *.exe
50 | *.log
51 | *.obj
52 | *.suo
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rewolf-jvm-operand-stack-viewer
2 | Java VM Operand Stack Viewer
3 |
4 | More info can be found here: http://blog.rewolf.pl/blog/?p=1301
5 |
--------------------------------------------------------------------------------
/java_opstack_viewer.rc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rwfpl/rewolf-jvm-operand-stack-viewer/a12fae57eae6729b48eca8e6fa3435e81758fbfe/java_opstack_viewer.rc
--------------------------------------------------------------------------------
/java_opstack_viewer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "java_opstack_viewer", "java_opstack_viewer.vcxproj", "{9ACA2BB0-0E1B-42D7-A2C7-12CD39BA2C39}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Win32 = Debug|Win32
11 | Debug|x64 = Debug|x64
12 | Release|Win32 = Release|Win32
13 | Release|x64 = Release|x64
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {9ACA2BB0-0E1B-42D7-A2C7-12CD39BA2C39}.Debug|Win32.ActiveCfg = Debug|Win32
17 | {9ACA2BB0-0E1B-42D7-A2C7-12CD39BA2C39}.Debug|Win32.Build.0 = Debug|Win32
18 | {9ACA2BB0-0E1B-42D7-A2C7-12CD39BA2C39}.Debug|x64.ActiveCfg = Debug|x64
19 | {9ACA2BB0-0E1B-42D7-A2C7-12CD39BA2C39}.Debug|x64.Build.0 = Debug|x64
20 | {9ACA2BB0-0E1B-42D7-A2C7-12CD39BA2C39}.Release|Win32.ActiveCfg = Release|Win32
21 | {9ACA2BB0-0E1B-42D7-A2C7-12CD39BA2C39}.Release|Win32.Build.0 = Release|Win32
22 | {9ACA2BB0-0E1B-42D7-A2C7-12CD39BA2C39}.Release|x64.ActiveCfg = Release|x64
23 | {9ACA2BB0-0E1B-42D7-A2C7-12CD39BA2C39}.Release|x64.Build.0 = Release|x64
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/java_opstack_viewer.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Debug
10 | x64
11 |
12 |
13 | Release
14 | Win32
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {9ACA2BB0-0E1B-42D7-A2C7-12CD39BA2C39}
23 | Win32Proj
24 | java_opstack_viewer
25 |
26 |
27 |
28 | Application
29 | true
30 | Unicode
31 | v120
32 |
33 |
34 | Application
35 | true
36 | Unicode
37 | v120
38 |
39 |
40 | Application
41 | false
42 | true
43 | Unicode
44 | v120
45 |
46 |
47 | Application
48 | false
49 | true
50 | Unicode
51 | v120
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | true
71 |
72 |
73 | true
74 |
75 |
76 | false
77 |
78 |
79 | false
80 |
81 |
82 |
83 |
84 |
85 | Level3
86 | Disabled
87 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
88 | MultiThreadedDebug
89 |
90 |
91 | Console
92 | true
93 |
94 |
95 |
96 |
97 |
98 |
99 | Level3
100 | Disabled
101 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
102 | MultiThreadedDebug
103 |
104 |
105 | Console
106 | true
107 |
108 |
109 |
110 |
111 | Level3
112 |
113 |
114 | MaxSpeed
115 | true
116 | true
117 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
118 | MultiThreaded
119 |
120 |
121 | Console
122 | true
123 | true
124 | true
125 |
126 |
127 |
128 |
129 | Level3
130 |
131 |
132 | MaxSpeed
133 | true
134 | true
135 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
136 | MultiThreaded
137 |
138 |
139 | Console
140 | true
141 | true
142 | true
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/java_opstack_viewer.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 |
23 |
24 | Header Files
25 |
26 |
27 |
28 |
29 | Resource Files
30 |
31 |
32 |
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * JVM Operand Stack Viewer
4 | *
5 | * Copyright (c) 2015 ReWolf
6 | * http://blog.rewolf.pl/
7 | *
8 | * This program is free software: you can redistribute it and/or modify
9 | * it under the terms of the GNU Lesser General Public License as published
10 | * by the Free Software Foundation, either version 3 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * This program is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU Lesser General Public License for more details.
17 | *
18 | * You should have received a copy of the GNU Lesser General Public License
19 | * along with this program. If not, see .
20 | *
21 | */
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include "resource.h"
29 |
30 | static const size_t maxLBStringSize = 0x100;
31 |
32 | #define DEFAULT_TIMER_INTERVAL 1000
33 | #define DEFAULT_TIMER_INTERVAL_WSTR L"1000"
34 | #define DEFAULT_TIMER_ID 666
35 |
36 | #ifndef _WIN64
37 | # define BITMASK 3
38 | #else
39 | # define BITMASK 7
40 | #endif
41 |
42 | #pragma pack(push, 1)
43 | struct JVMJITCode
44 | {
45 | DWORD_PTR unk01; // x64: 4; x86: 2
46 | DWORD_PTR unk02; // 1
47 | DWORD_PTR jvm_offset_BufferBlobVtable;
48 | DWORD_PTR jvm_offset_flush_icache_stub;
49 | DWORD unk_tab01[5];
50 | DWORD unk_ffffffff; // -1
51 | };
52 | #pragma pack(pop)
53 |
54 | class SmartHandle
55 | {
56 | public:
57 | SmartHandle(HANDLE h) : m_handle(h) { }
58 | ~SmartHandle() { if (INVALID_HANDLE_VALUE != m_handle) CloseHandle(m_handle); }
59 |
60 | operator HANDLE() const { return m_handle; }
61 |
62 | private:
63 | const HANDLE m_handle;
64 | };
65 |
66 | DWORD_PTR getThreadStackPtr(DWORD tid)
67 | {
68 | SmartHandle hTh = OpenThread(THREAD_ALL_ACCESS, FALSE, tid);
69 | DWORD_PTR trsp = 0;
70 | if ((0 != hTh) && (GetCurrentThreadId() != tid))
71 | {
72 | SuspendThread(hTh);
73 | CONTEXT ctx = { 0 };
74 | ctx.ContextFlags = CONTEXT_ALL;
75 | GetThreadContext(hTh, &ctx);
76 | ResumeThread(hTh);
77 | #ifndef _WIN64
78 | trsp = ctx.Esp;
79 | #else
80 | trsp = ctx.Rsp;
81 | #endif
82 | }
83 | return trsp;
84 | }
85 |
86 | DWORD getHexFromLB(HWND hWnd)
87 | {
88 | int csel = ListBox_GetCurSel(hWnd);
89 | if (LB_ERR == csel)
90 | return (DWORD)-1;
91 |
92 | wchar_t tstr[maxLBStringSize];
93 | if (LB_ERR == ListBox_GetText(hWnd, csel, tstr))
94 | return (DWORD)-1;
95 |
96 | DWORD ret = (DWORD)-1;
97 | swscanf_s(tstr, L"%08X", &ret);
98 | return ret;
99 | }
100 |
101 | DWORD_PTR getHexFromLB2(HWND hWnd)
102 | {
103 | int csel = ListBox_GetCurSel(hWnd);
104 | if (LB_ERR == csel)
105 | return (DWORD_PTR)-1;
106 |
107 | wchar_t tstr[maxLBStringSize];
108 | if (LB_ERR == ListBox_GetText(hWnd, csel, tstr))
109 | return (DWORD_PTR)-1;
110 |
111 | DWORD_PTR ret = (DWORD_PTR)-1;
112 | DWORD_PTR ret2 = (DWORD_PTR)-1;
113 | swscanf_s(tstr, L"%p: %p", &ret, &ret2);
114 | return ret2;
115 | }
116 |
117 | DWORD getCurrentPID(HWND HWnd)
118 | {
119 | return getHexFromLB(GetDlgItem(HWnd, LB_PROCESSES));
120 | }
121 |
122 | DWORD getCurrentTID(HWND HWnd)
123 | {
124 | return getHexFromLB(GetDlgItem(HWnd, LB_THREADS));
125 | }
126 |
127 | int VooDooCheckStandard(DWORD_PTR* stack, int i, DWORD_PTR ctx_rsp, HANDLE hProc)
128 | {
129 | if ((0 != i) && (stack[i] == ctx_rsp + i*sizeof(DWORD_PTR)))
130 | {
131 | for (int j = i - 1; j > 0; j--)
132 | {
133 | MEMORY_BASIC_INFORMATION mbi;
134 | VirtualQueryEx(hProc, (LPCVOID)stack[j], &mbi, sizeof(mbi));
135 | if ((PAGE_EXECUTE_READWRITE == mbi.Protect) &&
136 | (MEM_PRIVATE == mbi.Type))
137 | {
138 | JVMJITCode jit = { 0 };
139 | SIZE_T dummy;
140 | ReadProcessMemory(hProc, mbi.AllocationBase, &jit, sizeof(jit), &dummy);
141 | if (jit.unk_ffffffff == -1)
142 | {
143 | return j + 1;
144 | }
145 | }
146 | }
147 | }
148 | return -1;
149 | }
150 |
151 | int VooDooCheckMonitor(DWORD_PTR* stack, int i, DWORD_PTR ctx_rsp, HANDLE hProc)
152 | {
153 | const int maxMonitorDepth = min(i, 20) / 2;
154 | if ((i > 2) &&
155 | (stack[i] < ctx_rsp + i*sizeof(DWORD_PTR)) &&
156 | (stack[i] >= ctx_rsp + (i - maxMonitorDepth)*sizeof(DWORD_PTR)) &&
157 | ((stack[i] & BITMASK) == 0) &&
158 | ((ctx_rsp + i*sizeof(DWORD_PTR) - stack[i]) % (2*sizeof(DWORD_PTR)) == 0))
159 | {
160 | // Check only first monitor, maybe it's sufficient.
161 | DWORD_PTR monitorAddr = 0;
162 | SIZE_T dummy = 0;
163 | ReadProcessMemory(hProc, (LPCVOID)stack[i - 1], &monitorAddr, sizeof(monitorAddr), &dummy);
164 | if ((monitorAddr == ctx_rsp + (i - 2)*sizeof(DWORD_PTR)) ||
165 | (0 == stack[i - 1]))
166 | {
167 | for (int j = i - 3; j > 0; j--)
168 | {
169 | MEMORY_BASIC_INFORMATION mbi;
170 | VirtualQueryEx(hProc, (LPCVOID)stack[j], &mbi, sizeof(mbi));
171 | if ((PAGE_EXECUTE_READWRITE == mbi.Protect) &&
172 | (MEM_PRIVATE == mbi.Type))
173 | {
174 | JVMJITCode jit = { 0 };
175 | SIZE_T dummy;
176 | ReadProcessMemory(hProc, mbi.AllocationBase, &jit, sizeof(jit), &dummy);
177 | if (jit.unk_ffffffff == -1)
178 | {
179 | return j + 1;
180 | }
181 | }
182 | }
183 | }
184 | }
185 | return -1;
186 | }
187 |
188 | int doJVMOpStackVooDoo(DWORD_PTR* stack, size_t cnt, DWORD_PTR ctx_rsp, HANDLE hProc, std::function callback_on_each_entry)
189 | {
190 | int retStandard = -1;
191 | int retMonitor = -1;
192 | for (size_t i = 0; i < cnt; i++)
193 | {
194 | if (-1 == retStandard)
195 | {
196 | retStandard = VooDooCheckStandard(stack, (int)i, ctx_rsp, hProc);
197 |
198 | if (-1 == retMonitor)
199 | retMonitor = VooDooCheckMonitor(stack, (int)i, ctx_rsp, hProc);
200 | }
201 |
202 | callback_on_each_entry(i, stack[i]);
203 | }
204 | return (-1 != retStandard) ? retStandard : retMonitor;
205 | }
206 |
207 | void refreshMemory_internal(HWND hWnd, DWORD_PTR addr, DWORD lbID)
208 | {
209 | DWORD pid = getCurrentPID(hWnd);
210 | if ((DWORD)-1 == pid)
211 | return;
212 |
213 | HWND lbMem = GetDlgItem(hWnd, lbID);
214 | SendMessage(lbMem, WM_SETREDRAW, FALSE, 0);
215 | ListBox_ResetContent(lbMem);
216 |
217 | SmartHandle hProc = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid);
218 | if (INVALID_HANDLE_VALUE != hProc)
219 | {
220 | BYTE mem[0x1000];
221 | SIZE_T dummy = 0;
222 | MEMORY_BASIC_INFORMATION mbi = { 0 };
223 | VirtualQueryEx(hProc, (LPCVOID)addr, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
224 | size_t readSize = (DWORD_PTR)mbi.BaseAddress + mbi.RegionSize - addr;
225 | readSize = (readSize > sizeof(mem)) ? sizeof(mem) : readSize;
226 | if (ReadProcessMemory(hProc, (LPCVOID)addr, mem, readSize, &dummy))
227 | {
228 | wchar_t tstr[maxLBStringSize];
229 | swprintf_s(tstr, L"%08X %08X", mbi.AllocationProtect, mbi.Protect);
230 | ListBox_AddString(lbMem, tstr);
231 | for (SIZE_T i = 0; i < dummy; i+=8)
232 | {
233 | #define CAZ(n) (mem[i + (n)] > 0x1f) ? mem[i + (n)] : '.'
234 | swprintf_s(tstr, L"%p: %02X %02X %02X %02X %02X %02X %02X %02X | %c%c%c%c%c%c%c%c ",
235 | addr + i, mem[i], mem[i + 1], mem[i + 2], mem[i + 3], mem[i + 4], mem[i + 5], mem[i + 6], mem[i + 7],
236 | CAZ(0), CAZ(1), CAZ(2), CAZ(3), CAZ(4), CAZ(5), CAZ(6), CAZ(7));
237 | #undef CAZ
238 | ListBox_AddString(lbMem, tstr);
239 | }
240 | }
241 | }
242 |
243 | SendMessage(lbMem, WM_SETREDRAW, TRUE, 0);
244 | }
245 |
246 | void refreshMemory2(HWND hWnd)
247 | {
248 | wchar_t tstr[maxLBStringSize];
249 | tstr[0] = 0;
250 | GetDlgItemText(hWnd, EDT_ADDRESS, tstr, maxLBStringSize);
251 | DWORD_PTR addr = 0;
252 | swscanf_s(tstr, L"%p", &addr);
253 | refreshMemory_internal(hWnd, addr, LB_HEXMEM2);
254 | }
255 |
256 | void refreshMemory(HWND hWnd)
257 | {
258 | refreshMemory_internal(hWnd, getHexFromLB2(GetDlgItem(hWnd, LB_STACK)), LB_HEXMEM);
259 | }
260 |
261 | void refreshStack(HWND hWnd, int csel = -1, int topIdx = -1)
262 | {
263 | DWORD tid = getCurrentTID(hWnd);
264 | DWORD pid = getCurrentPID(hWnd);
265 | if (((DWORD)-1 == tid) || (GetCurrentThreadId() == tid) || ((DWORD)-1 == pid))
266 | return;
267 |
268 | HWND lbStack = GetDlgItem(hWnd, LB_STACK);
269 | SendMessage(lbStack, WM_SETREDRAW, FALSE, 0);
270 | ListBox_ResetContent(lbStack);
271 |
272 | SmartHandle hTh = OpenThread(THREAD_ALL_ACCESS, FALSE, tid);
273 | if (INVALID_HANDLE_VALUE != hTh)
274 | {
275 | SuspendThread(hTh);
276 | CONTEXT ctx = { 0 };
277 | ctx.ContextFlags = CONTEXT_ALL;
278 | GetThreadContext(hTh, &ctx);
279 |
280 | SmartHandle hProc = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid);
281 | if (INVALID_HANDLE_VALUE != hProc)
282 | {
283 | BYTE mem[0x10000];
284 | SIZE_T dummy = 0;
285 | MEMORY_BASIC_INFORMATION mbi = { 0 };
286 | #ifndef _WIN64
287 | DWORD stackPtr = ctx.Esp;
288 | #else
289 | DWORD64 stackPtr = ctx.Rsp;
290 | #endif
291 | VirtualQueryEx(hProc, (LPCVOID)stackPtr, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
292 | size_t readSize = (DWORD_PTR)mbi.BaseAddress + mbi.RegionSize - stackPtr;
293 | readSize = (readSize > sizeof(mem)) ? sizeof(mem) : readSize;
294 | bool stackFound = false;
295 | if (ReadProcessMemory(hProc, (LPCVOID)stackPtr, mem, readSize, &dummy))
296 | {
297 | csel = doJVMOpStackVooDoo((DWORD_PTR*)mem, dummy / sizeof(DWORD_PTR), stackPtr, hProc,
298 | [&stackPtr, &lbStack](size_t i, DWORD_PTR curV)
299 | {
300 | wchar_t tstr[maxLBStringSize];
301 | swprintf_s(tstr, L"%p: %p", stackPtr + i*sizeof(DWORD_PTR), curV);
302 | ListBox_AddString(lbStack, tstr);
303 | });
304 | if (-1 != topIdx)
305 | topIdx = (csel < 0) ? 0 : csel - 0;
306 | }
307 | }
308 | ResumeThread(hTh);
309 | }
310 |
311 | if (-1 != csel)
312 | {
313 | ListBox_SetCurSel(lbStack, csel);
314 | refreshMemory(hWnd);
315 | }
316 |
317 | if (-1 != topIdx)
318 | ListBox_SetTopIndex(lbStack, topIdx);
319 |
320 | SendMessage(lbStack, WM_SETREDRAW, TRUE, 0);
321 | }
322 |
323 | bool detectJVMStack(DWORD pid, DWORD_PTR ctx_rsp)
324 | {
325 | if (0 == ctx_rsp)
326 | return false;
327 |
328 | SmartHandle hProc = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid);
329 | if (INVALID_HANDLE_VALUE != hProc)
330 | {
331 | BYTE mem[0x10000];
332 | SIZE_T dummy = 0;
333 | MEMORY_BASIC_INFORMATION mbi = { 0 };
334 | VirtualQueryEx(hProc, (LPCVOID)ctx_rsp, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
335 | size_t readSize = (DWORD_PTR)mbi.BaseAddress + mbi.RegionSize - ctx_rsp;
336 | readSize = (readSize > sizeof(mem)) ? sizeof(mem) : readSize;
337 | if (ReadProcessMemory(hProc, (LPCVOID)ctx_rsp, mem, readSize, &dummy))
338 | {
339 | return -1 != doJVMOpStackVooDoo((DWORD_PTR*)mem, dummy / sizeof(DWORD_PTR), ctx_rsp, hProc, [](size_t, DWORD_PTR){});
340 | }
341 | }
342 | return false;
343 | }
344 |
345 | void refreshThreads(HWND hWnd)
346 | {
347 | HWND hLBThreads = GetDlgItem(hWnd, LB_THREADS);
348 | HWND hLBStack = GetDlgItem(hWnd, LB_STACK);
349 | HWND hLBMem = GetDlgItem(hWnd, LB_HEXMEM);
350 |
351 | DWORD pid = getCurrentPID(hWnd);
352 | if ((DWORD)-1 == pid)
353 | {
354 | ListBox_ResetContent(hLBThreads);
355 | ListBox_ResetContent(hLBStack);
356 | ListBox_ResetContent(hLBMem);
357 | return;
358 | }
359 |
360 | SmartHandle hToolHelp = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
361 | if (INVALID_HANDLE_VALUE == hToolHelp)
362 | return;
363 |
364 | DWORD ctid = getCurrentTID(hWnd);
365 |
366 | int stackCurSel = ListBox_GetCurSel(hLBStack);
367 | int stackTopIdx = ListBox_GetTopIndex(hLBStack);
368 |
369 | SendMessage(hLBThreads, WM_SETREDRAW, FALSE, 0);
370 | SendMessage(hLBStack, WM_SETREDRAW, FALSE, 0);
371 | SendMessage(hLBMem, WM_SETREDRAW, FALSE, 0);
372 |
373 | ListBox_ResetContent(hLBThreads);
374 | ListBox_ResetContent(hLBStack);
375 | ListBox_ResetContent(hLBMem);
376 |
377 | THREADENTRY32 thEntry = { 0 };
378 | thEntry.dwSize = sizeof(thEntry);
379 | if (Thread32First(hToolHelp, &thEntry))
380 | {
381 | do
382 | {
383 | if (pid == thEntry.th32OwnerProcessID)
384 | {
385 | DWORD_PTR trsp = getThreadStackPtr(thEntry.th32ThreadID);
386 |
387 | bool show = true;
388 | if (BST_CHECKED == SendDlgItemMessage(hWnd, CHK_THREADS_ONLY_JVM, BM_GETCHECK, 0, 0))
389 | {
390 | if (!detectJVMStack(pid, trsp))
391 | show = false;
392 | }
393 |
394 | if (show)
395 | {
396 | wchar_t tstr[maxLBStringSize];
397 | swprintf_s(tstr, L"%08X (%d)", thEntry.th32ThreadID, thEntry.th32ThreadID);
398 | ListBox_AddString(hLBThreads, tstr);
399 |
400 | if (ctid == thEntry.th32ThreadID)
401 | {
402 | ListBox_SetCurSel(hLBThreads, ListBox_GetCount(hLBThreads) - 1);
403 | refreshStack(hWnd, stackCurSel, stackTopIdx);
404 | }
405 | }
406 |
407 | }
408 | }
409 | while (Thread32Next(hToolHelp, &thEntry));
410 | }
411 |
412 | SendMessage(hLBThreads, WM_SETREDRAW, TRUE, 0);
413 | SendMessage(hLBStack, WM_SETREDRAW, TRUE, 0);
414 | SendMessage(hLBMem, WM_SETREDRAW, TRUE, 0);
415 | }
416 |
417 | void updateInterval(HWND hWnd)
418 | {
419 | DWORD v = GetDlgItemInt(hWnd, EDT_INTERVAL, nullptr, FALSE);
420 | if (0 != v)
421 | SetTimer(hWnd, DEFAULT_TIMER_ID, v, nullptr);
422 | else
423 | SetDlgItemText(hWnd, EDT_INTERVAL, DEFAULT_TIMER_INTERVAL_WSTR);
424 | }
425 |
426 | void refreshProcesses(HWND hWnd)
427 | {
428 | SmartHandle th32 = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
429 | if (INVALID_HANDLE_VALUE == th32)
430 | return;
431 |
432 | bool onlyJVM = false;
433 | if (BST_CHECKED == SendDlgItemMessage(hWnd, CHK_PROCESS_ONLY_JVM, BM_GETCHECK, 0, 0))
434 | onlyJVM = true;
435 |
436 | HWND lbProc = GetDlgItem(hWnd, LB_PROCESSES);
437 | ListBox_ResetContent(lbProc);
438 |
439 | PROCESSENTRY32 procEntry = { 0 };
440 | procEntry.dwSize = sizeof(procEntry);
441 | if (Process32First(th32, &procEntry))
442 | {
443 | do
444 | {
445 | bool show = true;
446 | if (onlyJVM)
447 | {
448 | show = false;
449 | THREADENTRY32 thEnt = { 0 };
450 | thEnt.dwSize = sizeof(thEnt);
451 | if (Thread32First(th32, &thEnt))
452 | {
453 | do
454 | {
455 | if ((thEnt.th32OwnerProcessID == procEntry.th32ProcessID) && detectJVMStack(procEntry.th32ProcessID, getThreadStackPtr(thEnt.th32ThreadID)))
456 | {
457 | show = true;
458 | break;
459 | }
460 | }
461 | while (Thread32Next(th32, &thEnt));
462 | }
463 | }
464 | if (show)
465 | {
466 | wchar_t tstr[maxLBStringSize];
467 | swprintf_s(tstr, L"%08X (%d) %ws", procEntry.th32ProcessID, procEntry.th32ProcessID, procEntry.szExeFile);
468 | ListBox_AddString(lbProc, tstr);
469 | }
470 | }
471 | while (Process32Next(th32, &procEntry));
472 | }
473 |
474 | refreshThreads(hWnd);
475 | }
476 |
477 | void onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
478 | {
479 | switch (wParam)
480 | {
481 | case MAKELPARAM(CHK_THREADS_ONLY_JVM, 0):
482 | case MAKELPARAM(LB_PROCESSES, LBN_SELCHANGE): refreshThreads(hWnd); break;
483 | case MAKELPARAM(LB_THREADS, LBN_SELCHANGE): refreshStack(hWnd, -1, 0); break;
484 | case MAKELPARAM(LB_STACK, LBN_SELCHANGE): refreshMemory(hWnd); break;
485 | case MAKELPARAM(CHK_PROCESS_ONLY_JVM, 0):
486 | case MAKELPARAM(BTN_PROCESS_REFRESH, 0): refreshProcesses(hWnd); break;
487 | case MAKELPARAM(EDT_ADDRESS, EN_CHANGE): refreshMemory2(hWnd); break;
488 | case MAKELPARAM(EDT_INTERVAL, EN_CHANGE): updateInterval(hWnd); break;
489 | }
490 | }
491 |
492 | void onWMInitDialog(HWND hWnd)
493 | {
494 | HGDIOBJ fixedFont = GetStockObject(SYSTEM_FIXED_FONT);
495 | SendDlgItemMessage(hWnd, LB_HEXMEM, WM_SETFONT, (WPARAM)fixedFont, TRUE);
496 | SendDlgItemMessage(hWnd, LB_HEXMEM2, WM_SETFONT, (WPARAM)fixedFont, TRUE);
497 | SendDlgItemMessage(hWnd, LB_STACK, WM_SETFONT, (WPARAM)fixedFont, TRUE);
498 | SendDlgItemMessage(hWnd, LB_PROCESSES, WM_SETFONT, (WPARAM)fixedFont, TRUE);
499 | SendDlgItemMessage(hWnd, LB_THREADS, WM_SETFONT, (WPARAM)fixedFont, TRUE);
500 |
501 | SendDlgItemMessage(hWnd, CHK_STACK_AUTO, BM_SETCHECK, BST_CHECKED, 0);
502 | SendDlgItemMessage(hWnd, CHK_THREADS_ONLY_JVM, BM_SETCHECK, BST_CHECKED, 0);
503 | SendDlgItemMessage(hWnd, CHK_PROCESS_ONLY_JVM, BM_SETCHECK, BST_CHECKED, 0);
504 |
505 | refreshProcesses(hWnd);
506 |
507 | SetDlgItemText(hWnd, EDT_INTERVAL, DEFAULT_TIMER_INTERVAL_WSTR);
508 | SetTimer(hWnd, DEFAULT_TIMER_ID, DEFAULT_TIMER_INTERVAL, 0);
509 | }
510 |
511 | void onWMTimer(HWND hWnd)
512 | {
513 | if (BST_CHECKED == SendDlgItemMessage(hWnd, CHK_STACK_AUTO, BM_GETCHECK, 0, 0))
514 | {
515 | refreshThreads(hWnd);
516 | refreshMemory2(hWnd);
517 | }
518 | }
519 |
520 | INT_PTR onWMCTLColorListBox(HWND hWnd, WPARAM wParam, LPARAM lParam)
521 | {
522 | SetBkMode((HDC)wParam, TRANSPARENT);
523 | SetTextColor((HDC)wParam, RGB(0xC0, 0xC0, 0xC0));
524 | return (INT_PTR)GetStockObject(BLACK_BRUSH);
525 | }
526 |
527 | INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
528 | {
529 | switch (uMsg)
530 | {
531 | case WM_CLOSE: EndDialog(hwndDlg, 0); break;
532 | case WM_INITDIALOG: onWMInitDialog(hwndDlg); break;
533 | case WM_COMMAND: onWMCommand(hwndDlg, wParam, lParam); break;
534 | case WM_TIMER: onWMTimer(hwndDlg); break;
535 | case WM_CTLCOLORLISTBOX: return onWMCTLColorListBox(hwndDlg, wParam, lParam);
536 | default: return FALSE;
537 | }
538 | return TRUE;
539 | }
540 |
541 | int wmain(int argc, wchar_t* argv[])
542 | {
543 | DialogBoxParam(GetModuleHandle(0), (LPCTSTR)DLG_MAIN, 0, DialogProc, 0);
544 | return 0;
545 | }
546 |
--------------------------------------------------------------------------------
/resource.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rwfpl/rewolf-jvm-operand-stack-viewer/a12fae57eae6729b48eca8e6fa3435e81758fbfe/resource.h
--------------------------------------------------------------------------------