diff --git a/.gitignore b/.gitignore index a280e6b..a5244fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,27 @@ -/.vs -*.ilk +*.ilk +*.pdb +*.exe +*.exp +*.lib +*.dll *.iobj *.ipdb -*.exe -*.dll +*.pdb +*.pyc *.pyd *.so *.o *.dylib *pycache* +*.lastcodeanalysissucceeded info.db +/.vs +/enc_temp_folder +leechrpc_h.h +leechrpc_c.c +leechrpc_s.c +/MemProcFs_files/temp +/MemProcFs_files/python36 /files/memprocfs /files/vmm_example.exe /files/lib @@ -17,6 +29,11 @@ info.db /files/ARM64 /files/x86/lib /files/x86/vmm_example.exe +/files/python36 +/files/vmmdll.h +/files/symsrv.yes +/files/leechcore.h +/temp Cargo.lock /vmmrust/memprocfs/target /vmmrust/memprocfs_example/target @@ -31,3 +48,9 @@ Cargo.lock /vmmsharp/vmmsharp/.vs /vmmsharp/vmmsharp/bin /vmmsharp/vmmsharp/obj +/vmm/vmm.aps +*.tmp +/files/vmm_example +/memtest/Debug +*.out +/vmmlx/vmmlxsym.nativecodeanalysis.xml diff --git a/README.md b/README.md index dbe1fc7..5131db1 100644 --- a/README.md +++ b/README.md @@ -241,11 +241,11 @@ v5.8 * Linux LeechAgent support using gRPC (LeechCore v2.21). * New FindEvil detection: High Entropy. * [DNS cache parsing](https://github.com/ufrisk/MemProcFS/wiki/FS_SysInfo_Network). Thanks [@MattCore71](https://github.com/MattCore71) for the contribution. - [v5.16](https://github.com/ufrisk/MemProcFS/releases/tag/v5.16) * Bug fixes. * Support for Windows 11 25H2 + [v5.17](https://github.com/ufrisk/MemProcFS/releases/tag/v5.17) * Support for Windows 11 26H1. @@ -261,7 +261,8 @@ v5.8 * PTE map upper limit adjusted from 65536 to 131072. * Fix: File writes in WSL mounted MemProcFS (drvfs) now works. -Latest: +[v5.19](https://github.com/ufrisk/MemProcFS/releases/tag/v5.19) +* Support for Windows 11 26H2. * Bug fixes. * Fix: Heap recovery now works on Windows 11 24H2 and later. * Fix: Pool and Heap parsing improved. diff --git a/includes/lib32/leechcore.lib b/includes/lib32/leechcore.lib index 61f8e0c..499942e 100644 Binary files a/includes/lib32/leechcore.lib and b/includes/lib32/leechcore.lib differ diff --git a/includes/lib32/vmm.lib b/includes/lib32/vmm.lib index 3a5273a..7bb7430 100644 Binary files a/includes/lib32/vmm.lib and b/includes/lib32/vmm.lib differ diff --git a/includes/lib64/leechcore.lib b/includes/lib64/leechcore.lib index d7777af..1e157dd 100644 Binary files a/includes/lib64/leechcore.lib and b/includes/lib64/leechcore.lib differ diff --git a/includes/lib64/vmm.lib b/includes/lib64/vmm.lib index 4c88c47..854195a 100644 Binary files a/includes/lib64/vmm.lib and b/includes/lib64/vmm.lib differ diff --git a/includes/libarm64/leechcore.lib b/includes/libarm64/leechcore.lib index b55f43d..8984f23 100644 Binary files a/includes/libarm64/leechcore.lib and b/includes/libarm64/leechcore.lib differ diff --git a/includes/libarm64/vmm.lib b/includes/libarm64/vmm.lib index 5ca86e6..8e76a74 100644 Binary files a/includes/libarm64/vmm.lib and b/includes/libarm64/vmm.lib differ diff --git a/m_vmemd/version.h b/m_vmemd/version.h index ffa8d62..4923aa0 100644 --- a/m_vmemd/version.h +++ b/m_vmemd/version.h @@ -2,9 +2,9 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 5 -#define VERSION_MINOR 18 -#define VERSION_REVISION 11 -#define VERSION_BUILD 250 +#define VERSION_MINOR 19 +#define VERSION_REVISION 0 +#define VERSION_BUILD 251 #define VER_FILE_DESCRIPTION_STR "MemProcFS : Plugin vmemd" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD diff --git a/memprocfs/version.h b/memprocfs/version.h index 47490d2..48b78da 100644 --- a/memprocfs/version.h +++ b/memprocfs/version.h @@ -2,9 +2,9 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 5 -#define VERSION_MINOR 18 -#define VERSION_REVISION 11 -#define VERSION_BUILD 250 +#define VERSION_MINOR 19 +#define VERSION_REVISION 0 +#define VERSION_BUILD 251 #define VER_FILE_DESCRIPTION_STR "MemProcFS" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD diff --git a/vmm/ext/sqlite3.c b/vmm/ext/sqlite3.c index 09f3e4a..0644a39 100644 --- a/vmm/ext/sqlite3.c +++ b/vmm/ext/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.53.3. By combining all the individual C code files into this +** version 3.53.4. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -18,7 +18,7 @@ ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in -** d4c0e51e4aeb96955b99185ab9cde75c339e with changes in files: +** bf7c7f30031888f4e796e429ab3978879485 with changes in files: ** ** */ @@ -467,12 +467,12 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.53.3" -#define SQLITE_VERSION_NUMBER 3053003 -#define SQLITE_SOURCE_ID "2026-06-26 20:14:12 d4c0e51e4aeb96955b99185ab9cde75c339e2c29c3f3f12428d364a10d782c62" +#define SQLITE_VERSION "3.53.4" +#define SQLITE_VERSION_NUMBER 3053004 +#define SQLITE_SOURCE_ID "2026-07-24 19:02:57 bf7c7f30031888f4e796e429ab3978879485813aaca6f641c7b33e4e09459bcc" #define SQLITE_SCM_BRANCH "branch-3.53" -#define SQLITE_SCM_TAGS "release version-3.53.3" -#define SQLITE_SCM_DATETIME "2026-06-26T20:14:12.354Z" +#define SQLITE_SCM_TAGS "release version-3.53.4" +#define SQLITE_SCM_DATETIME "2026-07-24T19:02:57.525Z" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -27591,7 +27591,7 @@ SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p }else{ double r; rc = pVfs->xCurrentTime(pVfs, &r); - *pTimeOut = (sqlite3_int64)(r*86400000.0); + *pTimeOut = sqlite3RealToI64(r*86400000.0); } return rc; } @@ -60969,7 +60969,7 @@ static int readSuperJournal(sqlite3_file *pJrnl, u64 nSuper, char **pzSuper){ cksum -= zOut[u]; } } - if( rc!=SQLITE_OK || cksum ){ + if( rc!=SQLITE_OK || cksum || zOut[0]==0 ){ /* If the checksum doesn't add up, then one or more of the disk sectors ** containing the super-journal filename is corrupted. This means ** definitely roll back, so just return SQLITE_OK and report a (nul) @@ -111437,11 +111437,20 @@ static SQLITE_NOINLINE void resolveSetExprSubtypeArg(ExprList *pList){ nn = pList ? pList->nExpr : 0; for(ii=0; iia[ii].pExpr; - ExprSetProperty(pExpr, EP_SubtArg); - if( pExpr->op==TK_SELECT ){ - assert( ExprUseXSelect(pExpr) ); - assert( pExpr->x.pSelect!=0 ); - resolveSetExprSubtypeArg(pExpr->x.pSelect->pEList); + while( 1 /*exit-by-break*/ ){ + ExprSetProperty(pExpr, EP_SubtArg); + if( pExpr->op==TK_SELECT ){ + assert( ExprUseXSelect(pExpr) ); + assert( pExpr->x.pSelect!=0 ); + resolveSetExprSubtypeArg(pExpr->x.pSelect->pEList); + break; + } + if( pExpr->op==TK_UPLUS ){ + pExpr = pExpr->pLeft; + assert( pExpr!=0 ); + }else{ + break; + } } } } @@ -176954,7 +176963,7 @@ static void nth_valueStepFunc( break; case SQLITE_FLOAT: { double fVal = sqlite3_value_double(apArg[1]); - if( ((i64)fVal)!=fVal ) goto error_out; + if( sqlite3RealToI64(fVal)!=fVal ) goto error_out; iVal = (i64)fVal; break; } @@ -210776,8 +210785,8 @@ static int fts3StringAppend( ** to grow the buffer until so that it is big enough to accommodate the ** appended data. */ - if( pStr->n+nAppend+1>=pStr->nAlloc ){ - sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100; + if( (i64)pStr->n+(i64)nAppend+1>=(i64)pStr->nAlloc ){ + i64 nAlloc = pStr->nAlloc+(i64)nAppend+100; char *zNew = sqlite3_realloc64(pStr->z, nAlloc); if( !zNew ){ return SQLITE_NOMEM; @@ -214981,7 +214990,8 @@ static u32 jsonTranslateBlobToText( if( sz==0 ) goto malformed_jsonb; if( zIn[0]=='-' ){ jsonAppendChar(pOut, '-'); - k++; + if( sz<=1 ) goto malformed_jsonb; + k = 1; } if( zIn[k]=='.' ){ jsonAppendChar(pOut, '0'); @@ -217945,7 +217955,9 @@ static int jsonSkipLabel(JsonEachCursor *p){ if( p->eType==JSONB_OBJECT ){ u32 sz = 0; u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz); - return p->i + n + sz; + sz += p->i + n; + if( sz >= p->sParse.nBlob ) sz = p->i; + return sz; }else{ return p->i; } @@ -226906,8 +226918,8 @@ static int rbuDeltaApply( int lenDelta, /* Length of the delta */ char *zOut /* Write the output into this preallocated buffer */ ){ - unsigned int limit; - unsigned int total = 0; + sqlite3_uint64 limit; + sqlite3_uint64 total = 0; #if RBU_ENABLE_DELTA_CKSUM char *zOrigOut = zOut; #endif @@ -226917,8 +226929,8 @@ static int rbuDeltaApply( /* ERROR: size integer not terminated by "\n" */ return -1; } - zDelta++; lenDelta--; - while( *zDelta && lenDelta>0 ){ + zDelta++; lenDelta--; /* Skip the \n */ + while( lenDelta>0 && zDelta[0] ){ unsigned int cnt, ofst; cnt = rbuDeltaGetInt(&zDelta, &lenDelta); if( lenDelta<=0 ) return -1; @@ -226926,7 +226938,7 @@ static int rbuDeltaApply( case '@': { zDelta++; lenDelta--; ofst = rbuDeltaGetInt(&zDelta, &lenDelta); - if( lenDelta>0 || zDelta[0]!=',' ){ + if( lenDelta>0 && zDelta[0]!=',' ){ /* ERROR: copy command not terminated by ',' */ return -1; } @@ -226951,7 +226963,7 @@ static int rbuDeltaApply( /* ERROR: insert command gives an output larger than predicted */ return -1; } - if( (i64)cnt>(i64)lenDelta ){ + if( cnt>lenDelta ){ /* ERROR: insert count exceeds size of delta */ return -1; } @@ -228964,13 +228976,13 @@ static int rbuGetUpdateStmt( char *zUpdate = 0; pUp->zMask = (char*)&pUp[1]; - memcpy(pUp->zMask, zMask, pIter->nTblCol); pUp->pNext = pIter->pRbuUpdate; pIter->pRbuUpdate = pUp; if( zSet ){ const char *zPrefix = ""; - + assert( p->rc==SQLITE_OK ); + memcpy(pUp->zMask, zMask, pIter->nTblCol); if( pIter->eType!=RBU_PK_VTAB ) zPrefix = "rbu_imp_"; zUpdate = sqlite3_mprintf("UPDATE \"%s%w\" SET %s WHERE %s", zPrefix, pIter->zTbl, zSet, zWhere @@ -229060,6 +229072,9 @@ static RbuState *rbuLoadState(sqlite3rbu *p){ case RBU_STATE_ROW: pRet->nRow = sqlite3_column_int(pStmt, 1); + if( pRet->nRow<0 ){ + rc = SQLITE_CORRUPT; + } break; case RBU_STATE_PROGRESS: @@ -240296,7 +240311,12 @@ static int sessionChangesetToHash( pIter->in.bNoDiscard = 1; while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ - rc = sessionOneChangeIterToHash(pGrp, pIter, bRebase); + if( bRebase && pIter->bPatchset ){ + /* A patchset may not be used as a rebase */ + rc = SQLITE_ERROR; + }else{ + rc = sessionOneChangeIterToHash(pGrp, pIter, bRebase); + } if( rc!=SQLITE_OK ) break; } @@ -240673,13 +240693,14 @@ static void sessionAppendPartialUpdate( int i; u8 *a1 = aRec; u8 *a2 = aChange; + u8 *a2Eof = &a2[nChange]; *pOut++ = SQLITE_UPDATE; *pOut++ = pIter->bIndirect; for(i=0; inCol; i++){ int n1 = sessionSerialLen(a1); - int n2 = sessionSerialLen(a2); - if( pIter->abPK[i] || a2[0]==0 ){ + int n2 = (a2>=a2Eof) ? 0 : sessionSerialLen(a2); + if( n2<=0 || pIter->abPK[i] || a2[0]==0 ){ if( !pIter->abPK[i] && a1[0] ) bData = 1; memcpy(pOut, a1, n1); pOut += n1; @@ -240880,8 +240901,8 @@ SQLITE_API int sqlite3rebaser_configure( sqlite3_rebaser *p, int nRebase, const void *pRebase ){ - sqlite3_changeset_iter *pIter = 0; /* Iterator opened on pData/nData */ int rc; /* Return code */ + sqlite3_changeset_iter *pIter = 0; /* Iterator opened on pData/nData */ rc = sqlite3changeset_start(&pIter, nRebase, (void*)pRebase); if( rc==SQLITE_OK ){ rc = sessionChangesetToHash(pIter, &p->grp, 1); @@ -251701,6 +251722,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ pRet = (Fts5Data*)sqlite3_malloc64(nAlloc); if( pRet ){ pRet->nn = nByte; + pRet->szLeaf = 0; aOut = pRet->p = (u8*)pRet + szData; }else{ rc = SQLITE_NOMEM; @@ -251713,10 +251735,8 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ sqlite3_free(pRet); pRet = 0; }else{ - /* TODO1: Fix this */ pRet->p[nByte] = 0x00; pRet->p[nByte+1] = 0x00; - pRet->szLeaf = fts5GetU16(&pRet->p[2]); } } p->rc = rc; @@ -251737,9 +251757,17 @@ static void fts5DataRelease(Fts5Data *pData){ sqlite3_free(pData); } +/* +** Read a leaf-page record. This is similar to fts5DataRead(), except that +** it fills in the Fts5Data.szLeaf value before returning. +*/ static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){ Fts5Data *pRet = fts5DataRead(p, iRowid); if( pRet ){ + assert( pRet->szLeaf==0 ); + if( pRet->nn>=4 ){ + pRet->szLeaf = fts5GetU16(&pRet->p[2]); + } if( pRet->szLeaf<4 || pRet->szLeaf>pRet->nn ){ FTS5_CORRUPT_ROWID(p, iRowid); fts5DataRelease(pRet); @@ -253649,6 +253677,10 @@ static void fts5SegIterNextInit( pIter->iPgidxOff = pIter->pLeaf->szLeaf; pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff); + if( iTermOff > pIter->pLeaf->szLeaf ){ + p->rc = FTS5_CORRUPT; + return; + } pIter->iLeafOffset = iTermOff; fts5SegIterLoadTerm(p, pIter, 0); fts5SegIterLoadNPos(p, pIter); @@ -255975,7 +256007,7 @@ static void fts5SecureDeleteOverflow( int iNext = 0; u8 *aPg = 0; - pLeaf = fts5DataRead(p, iRowid); + pLeaf = fts5LeafRead(p, iRowid); if( pLeaf==0 ) break; aPg = pLeaf->p; @@ -255983,7 +256015,7 @@ static void fts5SecureDeleteOverflow( if( iNext!=0 ){ *pbLastInDoclist = 0; } - if( iNext==0 && pLeaf->szLeaf!=pLeaf->nn ){ + if( iNext==0 && pLeaf->szLeafnn ){ fts5GetVarint32(&aPg[pLeaf->szLeaf], iNext); } @@ -256270,7 +256302,7 @@ static void fts5DoSecureDelete( /* The entry being removed may be the only position list in ** its doclist. */ for(iPgno=pSeg->iLeafPgno-1; iPgno>pSeg->iTermLeafPgno; iPgno-- ){ - Fts5Data *pPg = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno)); + Fts5Data *pPg = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno)); int bEmpty = (pPg && pPg->nn==4); fts5DataRelease(pPg); if( bEmpty==0 ) break; @@ -256278,7 +256310,7 @@ static void fts5DoSecureDelete( if( iPgno==pSeg->iTermLeafPgno ){ i64 iId = FTS5_SEGMENT_ROWID(iSegid, pSeg->iTermLeafPgno); - Fts5Data *pTerm = fts5DataRead(p, iId); + Fts5Data *pTerm = fts5LeafRead(p, iId); if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; int nTermIdx = pTerm->nn - pTerm->szLeaf; @@ -259230,7 +259262,7 @@ static void fts5IndexIntegrityCheckEmpty( /* Now check that the iter.nEmpty leaves following the current leaf ** (a) exist and (b) contain no terms. */ for(i=iFirst; p->rc==SQLITE_OK && i<=iLast; i++){ - Fts5Data *pLeaf = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i)); + Fts5Data *pLeaf = fts5LeafRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i)); if( pLeaf ){ if( !fts5LeafIsTermless(pLeaf) || (i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf)) @@ -259358,7 +259390,7 @@ static void fts5IndexIntegrityCheckSegment( FTS5_CORRUPT_ROWID(p, iRow); }else{ iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm); - if( iOff+nTerm>pLeaf->szLeaf ){ + if( (i64)iOff+(i64)nTerm>(i64)pLeaf->szLeaf ){ FTS5_CORRUPT_ROWID(p, iRow); }else{ res = fts5Memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm)); @@ -263086,19 +263118,23 @@ static int fts5ApiPhraseFirstColumn( if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ Fts5Sorter *pSorter = pCsr->pSorter; - int n; - if( pSorter ){ - int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); - n = pSorter->aIdx[iPhrase] - i1; - pIter->a = &pSorter->aPoslist[i1]; + if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){ + rc = SQLITE_RANGE; }else{ - rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n); - } - if( rc==SQLITE_OK ){ - assert( pIter->a || n==0 ); - pIter->b = (pIter->a ? &pIter->a[n] : 0); - *piCol = 0; - fts5ApiPhraseNextColumn(pCtx, pIter, piCol); + int n; + if( pSorter ){ + int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); + n = pSorter->aIdx[iPhrase] - i1; + pIter->a = &pSorter->aPoslist[i1]; + }else{ + rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n); + } + if( rc==SQLITE_OK ){ + assert( pIter->a || n==0 ); + pIter->b = (pIter->a ? &pIter->a[n] : 0); + *piCol = 0; + fts5ApiPhraseNextColumn(pCtx, pIter, piCol); + } } }else{ int n; @@ -264006,7 +264042,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2026-06-26 20:14:12 d4c0e51e4aeb96955b99185ab9cde75c339e2c29c3f3f12428d364a10d782c62", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2026-07-24 19:02:57 bf7c7f30031888f4e796e429ab3978879485813aaca6f641c7b33e4e09459bcc", -1, SQLITE_TRANSIENT); } /* diff --git a/vmm/ext/sqlite3.h b/vmm/ext/sqlite3.h index 00b22d9..c91b81c 100644 --- a/vmm/ext/sqlite3.h +++ b/vmm/ext/sqlite3.h @@ -146,12 +146,12 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.53.3" -#define SQLITE_VERSION_NUMBER 3053003 -#define SQLITE_SOURCE_ID "2026-06-26 20:14:12 d4c0e51e4aeb96955b99185ab9cde75c339e2c29c3f3f12428d364a10d782c62" +#define SQLITE_VERSION "3.53.4" +#define SQLITE_VERSION_NUMBER 3053004 +#define SQLITE_SOURCE_ID "2026-07-24 19:02:57 bf7c7f30031888f4e796e429ab3978879485813aaca6f641c7b33e4e09459bcc" #define SQLITE_SCM_BRANCH "branch-3.53" -#define SQLITE_SCM_TAGS "release version-3.53.3" -#define SQLITE_SCM_DATETIME "2026-06-26T20:14:12.354Z" +#define SQLITE_SCM_TAGS "release version-3.53.4" +#define SQLITE_SCM_DATETIME "2026-07-24T19:02:57.525Z" /* ** CAPI3REF: Run-Time Library Version Numbers diff --git a/vmm/infodb.c b/vmm/infodb.c index 76c4846..21bcdae 100644 --- a/vmm/infodb.c +++ b/vmm/infodb.c @@ -394,7 +394,7 @@ BOOL InfoDB_CertIsWellKnown(_In_ VMM_HANDLE H, _In_ QWORD qwThumbprintEndSHA1) QWORD qwResult = 0; POB_INFODB_CONTEXT pObCtx = NULL; qwThumbprintEndSHA1 = qwThumbprintEndSHA1 & 0x7fffffffffffffff; - if(!(pObCtx = ObContainer_GetOb(H->vmm.pObCInfoDB)) || !pObCtx->dwPdbId_NT) { goto fail; } + if(!(pObCtx = ObContainer_GetOb(H->vmm.pObCInfoDB))) { goto fail; } InfoDB_SqlQueryN(H, pObCtx, "SELECT count(*) FROM cert WHERE hash = ?", 1, &qwThumbprintEndSHA1, 1, &qwResult, NULL); fail: Ob_DECREF(pObCtx); diff --git a/vmm/modules/m_conf.c b/vmm/modules/m_conf.c index 8ca52ce..ab4a824 100644 --- a/vmm/modules/m_conf.c +++ b/vmm/modules/m_conf.c @@ -11,6 +11,31 @@ _Success_(return) BOOL VMMDLL_ConfigSet_Impl(_In_ VMM_HANDLE H, _In_ ULONG64 fOption, _In_ ULONG64 qwValue); +/* +* Cache the LeechCore memory map on first access for the module lifetime. +* CALLER DECREF: return +*/ +_Success_(return != NULL) +static POB_DATA MConf_MemMapGet(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP) +{ + POB_DATA pObMemMap = ObContainer_GetOb((POB_CONTAINER)ctxP->ctxM); + DWORD cbMemMap = 0; + PBYTE pbMemMap = NULL; + if(!pObMemMap) { + EnterCriticalSection(&H->vmm.LockPlugin); + pObMemMap = ObContainer_GetOb((POB_CONTAINER)ctxP->ctxM); + if(!pObMemMap) { + if(LcCommand(H->hLC, LC_CMD_MEMMAP_GET, 0, NULL, &pbMemMap, &cbMemMap) && pbMemMap) { + pObMemMap = ObData_New(H, pbMemMap, (DWORD)strnlen((LPSTR)pbMemMap, cbMemMap)); + ObContainer_SetOb((POB_CONTAINER)ctxP->ctxM, pObMemMap); + } + LcMemFree(pbMemMap); + } + LeaveCriticalSection(&H->vmm.LockPlugin); + } + return pObMemMap; +} + /* * Read : function as specified by the module manager. The module manager will * call into this callback function whenever a read shall occur from a "file". @@ -28,8 +53,17 @@ NTSTATUS MConf_Read(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _Out_wr CHAR szBuffer[0x800]; DWORD cbCallStatistics = 0; LPSTR szCallStatistics = NULL; + POB_DATA pObMemMap = NULL; QWORD cPageReadTotal, cPageFailTotal; NTSTATUS nt = VMMDLL_STATUS_FILE_INVALID; + if(!_stricmp(ctxP->uszPath, "config_leechcore_memmap.txt")) { + *pcbRead = 0; + if((pObMemMap = MConf_MemMapGet(H, ctxP))) { + nt = Util_VfsReadFile_FromObData(pObMemMap, pb, cb, pcbRead, cbOffset); + Ob_DECREF(pObMemMap); + } + return nt; + } if(!_stricmp(ctxP->uszPath, "config_process_show_terminated.txt")) { return Util_VfsReadFile_FromBOOL(H->vmm.flags & VMM_FLAG_PROCESS_SHOW_TERMINATED, pb, cb, pcbRead, cbOffset); } @@ -323,12 +357,17 @@ NTSTATUS MConf_Write(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _In_re */ BOOL MConf_List(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _Inout_ PHANDLE pFileList) { + POB_DATA pObMemMap = NULL; DWORD cbCallStatistics = 0; // not module root directory -> fail! if(ctxP->uszPath[0]) { return FALSE; } // "root" view if(!ctxP->pProcess) { Statistics_CallToString(H, NULL, &cbCallStatistics); + if((pObMemMap = MConf_MemMapGet(H, ctxP))) { + VMMDLL_VfsList_AddFile(pFileList, "config_leechcore_memmap.txt", pObMemMap->ObHdr.cbData, NULL); + Ob_DECREF(pObMemMap); + } VMMDLL_VfsList_AddFile(pFileList, "config_fileinfoheader_enable.txt", 1, NULL); VMMDLL_VfsList_AddFile(pFileList, "config_cache_enable.txt", 1, NULL); VMMDLL_VfsList_AddFile(pFileList, "config_paging_enable.txt", 1, NULL); @@ -362,6 +401,11 @@ BOOL MConf_List(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _Inout_ PHA return TRUE; } +static VOID MConf_Close(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP) +{ + Ob_DECREF((POB_CONTAINER)ctxP->ctxM); +} + /* * Initialization function. The module manager shall call into this function * when the module shall be initialized. If the module wish to initialize it @@ -374,11 +418,13 @@ BOOL MConf_List(_In_ VMM_HANDLE H, _In_ PVMMDLL_PLUGIN_CONTEXT ctxP, _Inout_ PHA VOID M_Conf_Initialize(_In_ VMM_HANDLE H, _Inout_ PVMMDLL_PLUGIN_REGINFO pRI) { if((pRI->magic != VMMDLL_PLUGIN_REGINFO_MAGIC) || (pRI->wVersion != VMMDLL_PLUGIN_REGINFO_VERSION)) { return; } + if(!(pRI->reg_info.ctxM = (PVMMDLL_PLUGIN_INTERNAL_CONTEXT)ObContainer_New())) { return; } // .status module is always valid - no check against pPluginRegInfo->tpMemoryModel, tpSystem strcpy_s(pRI->reg_info.uszPathName, 128, "\\conf"); // module name pRI->reg_info.fRootModule = TRUE; // module shows in root directory pRI->reg_fn.pfnList = MConf_List; // List function supported pRI->reg_fn.pfnRead = MConf_Read; // Read function supported pRI->reg_fn.pfnWrite = MConf_Write; // Write function supported + pRI->reg_fn.pfnClose = MConf_Close; pRI->pfnPluginManager_Register(H, pRI); } diff --git a/vmm/modules/m_sys_eventlog.c b/vmm/modules/m_sys_eventlog.c index c3348ad..36b1d18 100644 --- a/vmm/modules/m_sys_eventlog.c +++ b/vmm/modules/m_sys_eventlog.c @@ -2632,7 +2632,7 @@ static NTSTATUS VmmEventLog_Repair_Read(_In_ VMM_HANDLE H, _In_ PVMM_MAP_EVENTLO // VFS PRESENTATION // ============================================================================ #define MSYSEVENTLOG_LINELENGTH 256ULL -#define MSYSEVENTLOG_LINEHEADER " # PID Handle File Object Size Name Path" +#define MSYSEVENTLOG_LINEHEADER " # PID Handle File Object Size Name Path" #define MSYSEVENTLOG_FILE_EVENTLOGS "eventlogs.txt" #define MSYSEVENTLOG_DIR_ORIGINAL "eventlog_original" #define MSYSEVENTLOG_DIR_ORIGINAL_LEN VMM_STRLEN(MSYSEVENTLOG_DIR_ORIGINAL) diff --git a/vmm/oscompatibility.h b/vmm/oscompatibility.h index 2de9536..4a6bc87 100644 --- a/vmm/oscompatibility.h +++ b/vmm/oscompatibility.h @@ -69,6 +69,7 @@ errno_t fopen_su(FILE **pFile, const char *filename, const char *mode); #include #include #include +enum { OSCOMPAT_AF_INET6 = AF_INET6 }; #undef AF_INET6 #define AF_INET6 23 @@ -231,7 +232,7 @@ typedef int(*_CoreCrtNonSecureSearchSortCompareFunction)(void const *, void cons #define InterlockedIncrement(p) (__sync_add_and_fetch_4(p, 1)) #define InterlockedDecrement(p) (__sync_sub_and_fetch_4(p, 1)) #define GetCurrentProcess() ((HANDLE)-1) -#define InetNtopA(af,a,pb,cb) inet_ntop(((af)==23?10:(af)),a,pb,cb) +#define InetNtopA(af,a,pb,cb) inet_ntop(((af)==23?OSCOMPAT_AF_INET6:(af)),a,pb,cb) #define closesocket(s) close(s) #define HeapAlloc(hHeap, dwFlags, dwBytes) malloc(dwBytes) diff --git a/vmm/version.h b/vmm/version.h index d8a5553..24cddfc 100644 --- a/vmm/version.h +++ b/vmm/version.h @@ -2,9 +2,9 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 5 -#define VERSION_MINOR 18 -#define VERSION_REVISION 11 -#define VERSION_BUILD 250 +#define VERSION_MINOR 19 +#define VERSION_REVISION 0 +#define VERSION_BUILD 251 #define VER_FILE_DESCRIPTION_STR "MemProcFS : Core" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD diff --git a/vmm/vmm.h b/vmm/vmm.h index 0cf44eb..7a8779e 100644 --- a/vmm/vmm.h +++ b/vmm/vmm.h @@ -85,10 +85,18 @@ #define VMM_UADDR32_4(va) ((va) && (((va) & 0x80000003) == 0)) #define VMM_UADDR32_8(va) ((va) && (((va) & 0x80000007) == 0)) #define VMM_UADDR32_PAGE(va) ((va) && (((va) & 0x80000fff) == 0)) -#define VMM_KADDR64(va) (((va) & 0xffff800000000000) == 0xffff800000000000) -#define VMM_KADDR64_8(va) (((va) & 0xffff800000000007) == 0xffff800000000000) -#define VMM_KADDR64_16(va) (((va) & 0xffff80000000000f) == 0xffff800000000000) -#define VMM_KADDR64_PAGE(va) (((va) & 0xffff800000000fff) == 0xffff800000000000) +#define VMM_KADDR64_X64(va) (((va) & 0xffff800000000000) == 0xffff800000000000) +#define VMM_KADDR64_X64_8(va) (((va) & 0xffff800000000007) == 0xffff800000000000) +#define VMM_KADDR64_X64_16(va) (((va) & 0xffff80000000000f) == 0xffff800000000000) +#define VMM_KADDR64_X64_PAGE(va) (((va) & 0xffff800000000fff) == 0xffff800000000000) +#define VMM_KADDR64_LA57(va) (((va) & 0xff00000000000000) == 0xff00000000000000) +#define VMM_KADDR64_LA57_8(va) (((va) & 0xff00000000000007) == 0xff00000000000000) +#define VMM_KADDR64_LA57_16(va) (((va) & 0xff0000000000000f) == 0xff00000000000000) +#define VMM_KADDR64_LA57_PAGE(va) (((va) & 0xff00000000000fff) == 0xff00000000000000) +#define VMM_KADDR64(va) VMM_KADDR64_X64(va) +#define VMM_KADDR64_8(va) VMM_KADDR64_X64_8(va) +#define VMM_KADDR64_16(va) VMM_KADDR64_X64_16(va) +#define VMM_KADDR64_PAGE(va) VMM_KADDR64_X64_PAGE(va) #define VMM_UADDR64(va) ((va) && (((va) & 0xffff800000000000) == 0)) #define VMM_UADDR64_8(va) ((va) && (((va) & 0xffff800000000007) == 0)) #define VMM_UADDR64_16(va) ((va) && (((va) & 0xffff80000000000f) == 0)) diff --git a/vmm/vmmwinobj.c b/vmm/vmmwinobj.c index a309ed7..6cdaf71 100644 --- a/vmm/vmmwinobj.c +++ b/vmm/vmmwinobj.c @@ -545,7 +545,9 @@ VOID VmmWinObjFile_GetByProcess_DoWork(_In_ VMM_HANDLE H, _In_ POB_VMMWINOBJ_CON POB_SET psvaObFiles = NULL; PVMM_PROCESS pObSystemProcess = NULL; if(!(psvaObFiles = ObSet_New(H))) { return; } + LeaveCriticalSection(&ctx->LockUpdate); VmmWinObjFile_GetProcessAddressCandidates(H, ctx, pProcess, psvaObFiles, fHandles, TRUE); + EnterCriticalSection(&ctx->LockUpdate); // Fetch and initialize file objects if((pObSystemProcess = VmmProcessGet(H, 4))) { VmmWinObjFile_Initialize_FileObjects(H, ctx, pObSystemProcess, psvaObFiles, pmObFiles); @@ -689,20 +691,31 @@ VOID VmmWinObjFile_GetAll_DoWork(_In_ VMM_HANDLE H, _In_ POB_VMMWINOBJ_CONTEXT c if(!(pmObFiles = ObMap_New(H, OB_MAP_FLAGS_OBJECT_OB))) { goto fail; } if(!(pObSystemProcess = VmmProcessGet(H, 4))) { goto fail; } // fetch candicates from pool into cache: + if(ctx->fAll) { goto fail; } if(VmmMap_GetPool(H, &pObPoolMap, TRUE) && VmmMap_GetPoolTag(H, pObPoolMap, 'File', &pePoolTag)) { for(i = 0; i < pePoolTag->cEntry; i++) { pePool = pObPoolMap->pMap + pObPoolMap->piTag2Map[pePoolTag->iTag2Map + i]; if((pePool->cb < cbFile) || (pePool->cb > cbFile + 0x100)) { continue; } ObSet_Push(psvaObFiles, pePool->va); } - VmmWinObjFile_GetAll_DoWork_Pool(H, ctx, pObSystemProcess, psvaObFiles, pmObFiles); + EnterCriticalSection(&ctx->LockUpdate); + if(!ctx->fAll) { + VmmWinObjFile_GetAll_DoWork_Pool(H, ctx, pObSystemProcess, psvaObFiles, pmObFiles); + } + LeaveCriticalSection(&ctx->LockUpdate); } // fetch candidates from process handles & vads into cache: + if(ctx->fAll) { goto fail; } while((pObProcess = VmmProcessGetNext(H, pObProcess, 0))) { VmmWinObjFile_GetProcessAddressCandidates(H, ctx, pObProcess, psvaObFiles, TRUE, FALSE); VmmWinObjFile_GetProcessAddressCandidates(H, ctx, pObProcess, psvaObFiles, FALSE, FALSE); } - VmmWinObjFile_Initialize_FileObjects(H, ctx, pObSystemProcess, psvaObFiles, pmObFiles); + EnterCriticalSection(&ctx->LockUpdate); + if(!ctx->fAll) { + VmmWinObjFile_Initialize_FileObjects(H, ctx, pObSystemProcess, psvaObFiles, pmObFiles); + ctx->fAll = TRUE; + } + LeaveCriticalSection(&ctx->LockUpdate); fail: Ob_DECREF(pObSystemProcess); Ob_DECREF(psvaObFiles); @@ -739,16 +752,9 @@ BOOL VmmWinObjFile_GetAll(_In_ VMM_HANDLE H, _Out_ POB_MAP *ppmObFiles) *ppmObFiles = NULL; if(!(ctxOb = VmmWinObj_GetContext(H))) { goto finish; } if(ctxOb->fAll) { goto finish; } - EnterCriticalSection(&ctxOb->LockUpdate); - if(ctxOb->fAll) { - LeaveCriticalSection(&ctxOb->LockUpdate); - goto finish; - } VmmStatisticsLogStart(H, MID_OBJECT, LOGLEVEL_6_TRACE, NULL, &Statistics, "INIT FILE_OBJECT(ALL)"); VmmWinObjFile_GetAll_DoWork(H, ctxOb); VmmStatisticsLogEnd(H, &Statistics, "INIT FILE_OBJECT(ALL)"); - ctxOb->fAll = TRUE; - LeaveCriticalSection(&ctxOb->LockUpdate); finish: if(ctxOb && (*ppmObFiles = ObMap_New(H, OB_MAP_FLAGS_OBJECT_OB))) { ObMap_Filter(ctxOb->pmByObj, *ppmObFiles, (OB_MAP_FILTER_PFN_CB)VmmWinObjFile_GetAll_FilterFile); @@ -764,8 +770,7 @@ finish: // ---------------------------------------------------------------------------- /* -* Single-threaded worker function creating the object va -> pid mapping -* by walking all process handle tables/maps. +* Worker function creating the object va -> pid mapping by walking all process handle tables/maps. * -- H * -- ctx */ @@ -774,16 +779,17 @@ VOID VmmWinObj_GetProcessAssociated_DoWork(_In_ VMM_HANDLE H, _In_ POB_VMMWINOBJ DWORD i, dwPID; POB_VMMWINOBJ_FILE pObFile = NULL; POB_MAP pmObFile = NULL; - POB_COUNTER pcVaToPid = NULL; + POB_COUNTER pcObVaToPid = NULL; PVMM_PROCESS pObProcess = NULL; PVMMOB_MAP_HANDLE pObHandleMap = NULL; if(ctx->pcVaToPid || H->fAbort) { return; } - if(!(pcVaToPid = ObCounter_New(H, 0))) { return; } + if(!(pcObVaToPid = ObCounter_New(H, 0))) { return; } // 1: add process object handles to map: while((pObProcess = VmmProcessGetNext(H, pObProcess, 0))) { if(VmmMap_GetHandle(H, pObProcess, &pObHandleMap, VMM_HANDLE_FLAG_CORE)) { + if(ctx->pcVaToPid) { goto fail; } for(i = 0; i < pObHandleMap->cMap; i++) { - ObCounter_Set(pcVaToPid, pObHandleMap->pMap[i].vaObject, pObProcess->dwPID); + ObCounter_Set(pcObVaToPid, pObHandleMap->pMap[i].vaObject, pObProcess->dwPID); } Ob_DECREF_NULL(&pObHandleMap); } @@ -793,22 +799,34 @@ VOID VmmWinObj_GetProcessAssociated_DoWork(_In_ VMM_HANDLE H, _In_ POB_VMMWINOBJ // also missing file objects to map. if(VmmWinObjFile_GetAll(H, &pmObFile)) { while((pObFile = ObMap_GetNext(pmObFile, pObFile))) { - dwPID = (DWORD)ObCounter_Get(pcVaToPid, pObFile->va); + if(ctx->pcVaToPid) { goto fail; } + dwPID = (DWORD)ObCounter_Get(pcObVaToPid, pObFile->va); if(dwPID && pObFile->vaSectionObjectPointers) { - ObCounter_Set(pcVaToPid, pObFile->vaSectionObjectPointers, dwPID); + ObCounter_Set(pcObVaToPid, pObFile->vaSectionObjectPointers, dwPID); } } while((pObFile = ObMap_GetNext(pmObFile, pObFile))) { - if(!ObCounter_Exists(pcVaToPid, pObFile->va) && pObFile->vaSectionObjectPointers) { - dwPID = (DWORD)ObCounter_Get(pcVaToPid, pObFile->vaSectionObjectPointers); + if(ctx->pcVaToPid) { goto fail; } + if(!ObCounter_Exists(pcObVaToPid, pObFile->va) && pObFile->vaSectionObjectPointers) { + dwPID = (DWORD)ObCounter_Get(pcObVaToPid, pObFile->vaSectionObjectPointers); if(dwPID) { - ObCounter_Set(pcVaToPid, pObFile->va, dwPID); + ObCounter_Set(pcObVaToPid, pObFile->va, dwPID); } } } } + EnterCriticalSection(&ctx->LockUpdate); + if(!ctx->pcVaToPid) { + ctx->pcVaToPid = pcObVaToPid; + pcObVaToPid = NULL; + } + LeaveCriticalSection(&ctx->LockUpdate); +fail: + Ob_DECREF(pObFile); Ob_DECREF(pmObFile); - ctx->pcVaToPid = pcVaToPid; + Ob_DECREF(pObProcess); + Ob_DECREF(pcObVaToPid); + Ob_DECREF(pObHandleMap); } /* @@ -828,9 +846,7 @@ PVMM_PROCESS VmmWinObj_GetProcessAssociated(_In_ VMM_HANDLE H, _In_ QWORD vaObje if(!(ctxOb = VmmWinObj_GetContext(H))) { return NULL; } // create new va->pid mapping if not already created: if(!ctxOb->pcVaToPid) { - EnterCriticalSection(&ctxOb->LockUpdate); VmmWinObj_GetProcessAssociated_DoWork(H, ctxOb); - LeaveCriticalSection(&ctxOb->LockUpdate); } // finish up and return process (if found): dwPID = (DWORD)ObCounter_Get(ctxOb->pcVaToPid, vaObject); diff --git a/vmmpyc/version.h b/vmmpyc/version.h index e6919e0..37a8d41 100644 --- a/vmmpyc/version.h +++ b/vmmpyc/version.h @@ -2,9 +2,9 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 5 -#define VERSION_MINOR 18 -#define VERSION_REVISION 11 -#define VERSION_BUILD 250 +#define VERSION_MINOR 19 +#define VERSION_REVISION 0 +#define VERSION_BUILD 251 #define VER_FILE_DESCRIPTION_STR "MemProcFS : Python API" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD diff --git a/vmmrust/leechcore_example/Cargo.toml b/vmmrust/leechcore_example/Cargo.toml index 0019d93..3b9e9dc 100644 --- a/vmmrust/leechcore_example/Cargo.toml +++ b/vmmrust/leechcore_example/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "leechcore_example" -version = "5.18.11" +version = "5.19.0" edition = "2021" publish = false diff --git a/vmmrust/m_example_plugin/Cargo.toml b/vmmrust/m_example_plugin/Cargo.toml index 0952928..fe6fea4 100644 --- a/vmmrust/m_example_plugin/Cargo.toml +++ b/vmmrust/m_example_plugin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "m_example_plugin" -version = "5.18.11" +version = "5.19.0" edition = "2021" publish = false diff --git a/vmmrust/memprocfs/Cargo.toml b/vmmrust/memprocfs/Cargo.toml index 0242fd6..424ac02 100644 --- a/vmmrust/memprocfs/Cargo.toml +++ b/vmmrust/memprocfs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "memprocfs" -version = "5.18.11" +version = "5.19.0" edition = "2021" description = "MemProcFS - Physical Memory Analysis Framework" documentation = "https://docs.rs/memprocfs" diff --git a/vmmrust/memprocfs_example/Cargo.toml b/vmmrust/memprocfs_example/Cargo.toml index 1316fff..1e4a624 100644 --- a/vmmrust/memprocfs_example/Cargo.toml +++ b/vmmrust/memprocfs_example/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "memprocfs_example" -version = "5.18.11" +version = "5.19.0" edition = "2021" publish = false diff --git a/vmmsharp/vmmsharp/Properties/AssemblyInfo.cs b/vmmsharp/vmmsharp/Properties/AssemblyInfo.cs index 1bfb3a9..81e007f 100644 --- a/vmmsharp/vmmsharp/Properties/AssemblyInfo.cs +++ b/vmmsharp/vmmsharp/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.Versioning; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("5.18.11.250")] -[assembly: AssemblyFileVersion("5.18.11.250")] +[assembly: AssemblyVersion("5.19.0.251")] +[assembly: AssemblyFileVersion("5.19.0.251")] diff --git a/vmmsharp/vmmsharp/vmmsharp.csproj b/vmmsharp/vmmsharp/vmmsharp.csproj index 16eda76..eb3927c 100644 --- a/vmmsharp/vmmsharp/vmmsharp.csproj +++ b/vmmsharp/vmmsharp/vmmsharp.csproj @@ -1,6 +1,6 @@ - net48;net5.0;net6.0;net7.0;net8.0;net9.0 + net48;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0 AnyCPU Library Vmmsharp @@ -24,7 +24,7 @@ MemProcFS : C# API Ulf Frisk vmmsharp - Copyright © Ulf Frisk 2024 + Copyright © Ulf Frisk 2024-2026 3.3.0.7 3.3.0.7 x64 @@ -109,7 +109,7 @@ - 5.18.11 + 5.19.0 https://github.com/ufrisk/MemProcFS git LICENSE @@ -119,5 +119,17 @@ true logo.png Vmmsharp is a C# library for the MemProcFS memory analysis framework. + + + full + + + full + + + full + + + full