It does not run successfully in a 32-bit environment and is recorded as a crash.
fish3rman opened this issue · 4 comments
Hi!
I made test binary referenced by #106 .
It works properly in a 64-bit binary, but something wrong with 32-bit.
The strange thing is that when tracing, the address of reader.exe
is not recorded.
Instead of this, the start function in input.trace
is nt!KiGeneralProtectionFault
Code
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#define BUFFERSIZE 5
DWORD g_BytesTransferred = 0;
void DisplayError(LPTSTR lpszFunction);
VOID CALLBACK FileIOCompletionRoutine(
__in DWORD dwErrorCode,
__in DWORD dwNumberOfBytesTransfered,
__in LPOVERLAPPED lpOverlapped
);
VOID CALLBACK FileIOCompletionRoutine(
__in DWORD dwErrorCode,
__in DWORD dwNumberOfBytesTransfered,
__in LPOVERLAPPED lpOverlapped)
{
_tprintf(TEXT("Error code:\t%x\n"), dwErrorCode);
_tprintf(TEXT("Number of bytes:\t%x\n"), dwNumberOfBytesTransfered);
g_BytesTransferred = dwNumberOfBytesTransfered;
}
//
// Note: this simplified sample assumes the file to read is an ANSI text file
// only for the purposes of output to the screen. CreateFile and ReadFile
// do not use parameters to differentiate between text and binary file types.
//
int __cdecl _tmain(int argc, TCHAR* argv[])
{
HANDLE hFile;
DWORD dwBytesRead = 0;
char ReadBuffer[BUFFERSIZE] = { 0 };
OVERLAPPED ol = { 0 };
printf("\n");
if (argc != 2)
{
printf("Usage Error: Incorrect number of arguments\n\n");
_tprintf(TEXT("Usage:\n\t%s <text_file_name>\n"), argv[0]);
return 0;
}
int tmp;
scanf("%d", &tmp);
hFile = CreateFile(argv[1], // file to open
GENERIC_READ, // open for reading
FILE_SHARE_READ, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // normal file
NULL); // no attr. template
if (hFile == INVALID_HANDLE_VALUE)
{
DisplayError(const_cast<LPTSTR>(TEXT("CreateFile")));
_tprintf(TEXT("Terminal failure: unable to open file \"%s\" for read.\n"), argv[1]);
return 0;
}
// Read one character less than the buffer size to save room for
// the terminating NULL character.
if (FALSE == ReadFileEx(hFile, ReadBuffer, BUFFERSIZE - 1, &ol, FileIOCompletionRoutine))
{
DisplayError(const_cast<LPTSTR>(TEXT("ReadFile")));
printf("Terminal failure: Unable to read from file.\n GetLastError=%08x\n", GetLastError());
CloseHandle(hFile);
return 0;
}
SleepEx(5000, TRUE);
dwBytesRead = g_BytesTransferred;
// This is the section of code that assumes the file is ANSI text.
// Modify this block for other data types if needed.
if (dwBytesRead > 0 && dwBytesRead <= BUFFERSIZE - 1)
{
ReadBuffer[dwBytesRead] = '\0'; // NULL character
_tprintf(TEXT("Data read from %s (%d bytes): \n"), argv[1], dwBytesRead);
printf("%s\n", ReadBuffer);
}
else if (dwBytesRead == 0)
{
_tprintf(TEXT("No data read from file %s\n"), argv[1]);
}
else
{
printf("\n ** Unexpected value for dwBytesRead ** \n");
}
// It is always good practice to close the open file handles even though
// the app will exit here and clean up open handles anyway.
CloseHandle(hFile);
}
void DisplayError(LPTSTR lpszFunction)
// Routine Description:
// Retrieve and output the system error message for the last-error code
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL);
lpDisplayBuf =
(LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)
+ lstrlen((LPCTSTR)lpszFunction)
+ 40) // account for format string
* sizeof(TCHAR));
if (FAILED(StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error code %d as follows:\n%s"),
lpszFunction,
dw,
lpMsgBuf)))
{
printf("FATAL ERROR: Unable to output error code.\n");
}
_tprintf(TEXT("ERROR: %s\n"), (LPCTSTR)lpDisplayBuf);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
harness
// Disable pagefiles https://www.tomshardware.com/reviews/ssd-performance-tweak,2911-4.html
#include "backend.h"
#include "crash_detection_umode.h"
#include "fshandle_table.h"
#include "fshooks.h"
#include "targets.h"
#include <fmt/format.h>
#include <iostream>
#include <string>
#include <codecvt>
#include <locale>
namespace Reader {
constexpr bool LoggingOn = true;
template <typename... Args_t>
void DebugPrint(const char *Format, const Args_t &...args) {
if constexpr (LoggingOn) {
fmt::print("reader: ");
fmt::print(fmt::runtime(Format), args...);
}
}
std::string u16stringToString(const std::u16string& u16str) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
return convert.to_bytes(u16str);
}
bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) {
g_FsHandleTable.MapExistingGuestFile(
uR"(\??\C:\bin\readme.txt)", Buffer, BufferSize);
return true;
}
bool Init(const Options_t &Opts, const CpuState_t &State) {
const Gva_t Rip = Gva_t(g_Backend->Rip());
const Gva_t AfterCalls = Rip + Gva_t(0x3);
if (!g_Backend->SetBreakpoint(AfterCalls, [](Backend_t *Backend) {
DebugPrint("Reached function end\n");
Backend->PrintRegisters();
Backend->Stop(Ok_t());
})) {
DebugPrint("Failed to SetBreakpoint AfterCalls\n");
return false;
}
if (!g_Backend->SetBreakpoint("reader!wprintf", [](Backend_t *Backend) {
const Gva_t FormatPtr = Backend->GetArgGva(0);
const std::u16string &WFormat = Backend->VirtReadWideString(FormatPtr);
const std::string &Format = u16stringToString(WFormat);
DebugPrint("wprintf: {}", Format);
Backend->SimulateReturnFromFunction(0);
})) {
DebugPrint("Failed to SetBreakpoint on printf\n");
return false;
}
if (!g_Backend->SetBreakpoint("reader!printf", [](Backend_t *Backend) {
const Gva_t FormatPtr = Backend->GetArgGva(0);
const std::string &Format = Backend->VirtReadString(FormatPtr);
DebugPrint("printf: {}", Format);
Backend->SimulateReturnFromFunction(0);
})) {
fmt::print("Failed to SetBreakpoint on printf\n");
return false;
}
if (!SetupFilesystemHooks()) {
DebugPrint("Failed to SetupFilesystemHooks\n");
return false;
}
if (!SetupUsermodeCrashDetectionHooks()) {
fmt::print("Failed to SetupUsermodeCrashDetectionHooks\n");
return false;
}
return true;
}
Target_t Reader("reader", Init, InsertTestcase);
}
- Dump
kd> !process 0 0 reader.exe
PROCESS ffffd88a3219f080
SessionId: 2 Cid: 1214 Peb: 00340000 ParentCid: 19fc
DirBase: 13746000 ObjectTable: ffffbf0d16027d00 HandleCount: 50.
Image: reader.exe
kd> .process /i /p ffffd88a3219f080
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!DbgBreakPointWithStatus:
fffff804`53bff050 cc int 3
kd> .reload /user
Loading User Symbols
.....
kd> lmsmu
start end module name
00007ff9`b0ff0000 00007ff9`b11e5000 ntdll (deferred)
00000000`008e0000 00000000`008e7000 reader (deferred)
00007ff9`b0e00000 00007ff9`b0e59000 wow64 (deferred)
00000000`77d70000 00000000`77d7a000 wow64cpu (deferred)
00007ff9`af900000 00007ff9`af983000 wow64win (deferred)
kd> u 00000000`008e0000 + 11d9
*** WARNING: Unable to verify checksum for reader.exe
reader!wmain+0x79 [C:\wtf\targets\reader\harness\reader\main.cpp @ 50]:
00000000`008e11d9 83c408 add esp,8
00000000`008e11dc 6a00 push 0
00000000`008e11de 6880000040 push 40000080h
00000000`008e11e3 6a03 push 3
00000000`008e11e5 6a00 push 0
00000000`008e11e7 6a01 push 1
00000000`008e11e9 6800000080 push 0FFFFFFFF80000000h
00000000`008e11ee ff7604 push qword ptr [rsi+4]
kd> bp 00000000`008e0000 + 11d9
kd> g
The context is partially valid. Only x86 user-mode context is available.
Breakpoint 0 hit
reader!wmain+0x79:
00000000`008e11d9 83c408 add esp,8
32.kd:x86> bc *
32.kd:x86> !wow64exts.sw
Switched to Host mode
32.kd> !snapshot c:\dump
[dbgeng-rs] Dumping the CPU state into c:\dump\state.19041.1.amd64fre.vb_release.191206-1406.20240807_0540\regs.json..
[dbgeng-rs] Dumping the memory state into c:\dump\state.19041.1.amd64fre.vb_release.191206-1406.20240807_0540\mem.dmp..
Creating c:\\dump\\state.19041.1.amd64fre.vb_release.191206-1406.20240807_0540\\mem.dmp - Full memory range dump
0% written.
5% written. 40 sec remaining.
ValidateSequenceNumber: Sequence number too far ahead for validation.
10% written. 35 sec remaining.
15% written. 30 sec remaining.
20% written. 30 sec remaining.
25% written. 27 sec remaining.
30% written. 26 sec remaining.
35% written. 24 sec remaining.
40% written. 19 sec remaining.
45% written. 17 sec remaining.
50% written. 16 sec remaining.
55% written. 14 sec remaining.
60% written. 12 sec remaining.
65% written. 11 sec remaining.
70% written. 9 sec remaining.
75% written. 8 sec remaining.
80% written. 7 sec remaining.
85% written. 5 sec remaining.
90% written. 3 sec remaining.
95% written. 1 sec remaining.
Wrote 4.0 GB in 35 sec.
The average transfer rate was 117.0 MB/s.
Dump successfully written
[dbgeng-rs] Done!
- Result
C:\wtf\targets\reader>..\..\src\build\wtf.exe run --name reader --state state --backend=bochscpu --input .\inputs\input --trace-type 1
Initializing the debugger instance.. (this takes a bit of time)
Setting debug register status to zero.
Setting debug register status to zero.
Trace file C:\wtf\targets\reader\input.trace
Running .\inputs\input
Mapping already existing guest file \??\C:\bin\readme.txt with filestream(21)
--------------------------------------------------
Run stats:
Instructions executed: 4.1k (2.0k unique)
Dirty pages: 36.0kb
Memory accesses: 19.3kb
Edges executed: 0.0 (0.0 unique)
#1 cov: 1988 exec/s: 1.0 lastcov: 0.0s crash: 1 timeout: 0 cr3: 0 uptime: 1.0s
- Symbolizer
ntoskrnl.exe+0x00404cc0
ntoskrnl.exe+0x00404cc1
ntoskrnl.exe+0x00404cc8
ntoskrnl.exe+0x00404cd0
ntoskrnl.exe+0x00404cd4
ntoskrnl.exe+0x00404cd8
ntoskrnl.exe+0x00404cdc
ntoskrnl.exe+0x00404ce0
ntoskrnl.exe+0x00404ce4
ntoskrnl.exe+0x00404ce8
ntoskrnl.exe+0x00404cec
ntoskrnl.exe+0x00404cf0
ntoskrnl.exe+0x00404cf7
ntoskrnl.exe+0x00404d26
ntoskrnl.exe+0x00404d2d
ntoskrnl.exe+0x00404d2f
...
-
Environment
- Windows 10 21H2(OS Build 19044.1288), Hyper-v with 4GB, 1core
- Disable pagefile + lockmem + Disabled KVA shadow via disable-kva.cmd
-
Attachment
https://drive.google.com/file/d/1yVPfTyjE-bWZktyoZ1OFxg9ZmWwjBJ60/view?usp=sharing
Thanks for the detailed report 🙏🏽
There were some issues recently around Wow64 that might / might not be related:
- One bug in
snapshot
/dbgeng-rs
(0vercl0k/snapshot#8) fixed by 0vercl0k/dbgeng-rs@0b13186 symbolizer-rs
didn't extract the Wow64 modules off the kernel dump (0vercl0k/addr-symbolizer-rs#1); fix is up in 0vercl0k/addr-symbolizer-rs@a1c2887 but not merged yet
So basically, you should make sure you updated snapshot
to the latest version to grab your dump file and if you want to use symbolizer-rs
you should checkout the fbl_libify
branch and build it yourself with (cargo build --release
).
I'll check the code / your results more closely this week though.
Cheers
Okay I looked closer at your regs.json
and I can see that the segment limit is 0xfffff
instead of 0xffffffff
so I think you updating snapshot to >= 0.2.2
will resolve your issue; I believe you are hitting 0vercl0k/snapshot#8.
Cheers
yes the problem was the old version of snapshot.
thanks 👍
Awesome, sorry for the bug!
Cheers