├── .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 --------------------------------------------------------------------------------