• XSS.stack #1 – первый литературный журнал от юзеров форума

How does pointer scanning work exactly...?

icecat89

CD-диск
Пользователь
Регистрация
18.09.2024
Сообщения
12
Реакции
0
about a week now, i was wondering how does " pointer scanning " work exactly, it's not that type of question of how to use it in cheat engine or something, but how in the code work, I spend some time for this week to learn how virtual memory work, some known win32 api functions too, i asked the developer of cheat engine:

borya — 27/02/2025 01:31
is it like tryin to get all pointers and derefrencing them?
DB — 27/02/2025 01:31
kinda yes, but then the opposite way. It finds all the pointers that reference the specific address, and then goes back to those pointers and repeats
borya — 27/02/2025 01:32
what
how this is even possible
DB — 27/02/2025 01:33
A pointer is just a 4/8 byte value representing a memory address. If you have a memory address you can scan for a value between that value and that value minus the maximum offset you wish to check for

borya — 28/02/2025 01:43
isn't like starting from base adress + some random memory then derefrence until you find a value will be easier?
how can you start from the target adress until the base adress?

DB — 28/02/2025 18:47
see it like raytracing. Instead of going from a lightsource following every possible path until a screen pixel is hit, go from a screenpixel and follow the paths until a lightsource is detected

Same for pointerscan. You can start from a static address but the chance you start with a base address that eventually leads to your destination is astronomically small. And it will take a lot of pathfollowing to figure that out
If you start with the destination then at least all the paths inspected have at least something to do with the destination.



does anyone have any idea what did he mean? i searched a little bit and got an idea
that we need to read all the Virtual memory of that process, well the stack and the heap, so we need something like VirtualQueryEx since it fills the MEMORY_BASIC_INFORMATION struct that has can we check through, what region or section we are in :
1740946804994.png

so we need like to check after using something like:
C++:
MEMORY_BASIC_INFORMATION mbinfo;
memset(&mbi, 0, sizeof(mbinfo));
/*
// then calling her VirtualQueryEx
*/

// now checking if that pages are commited and have permission at least READ_WRITE
if ((mbinfo.state == MEM_COMMIT) && (mbinfo.Protect & PAGE_READWRITE) ){
    //do something
}


i don't know too much about windows, it's been like a week only or maybee two, the main thing i find is that this process of "pointer scaning " has a big relation with PEB,TEB and the kernel struct of KUSER_SHARED_DATA.
C++:
typedef struct _KUSER_SHARED_DATA {
  ULONG                         TickCountLowDeprecated;
  ULONG                         TickCountMultiplier;
  KSYSTEM_TIME                  InterruptTime;
  KSYSTEM_TIME                  SystemTime;
  KSYSTEM_TIME                  TimeZoneBias;
  USHORT                        ImageNumberLow;
  USHORT                        ImageNumberHigh;
  WCHAR                         NtSystemRoot[260];
  ULONG                         MaxStackTraceDepth;
  ULONG                         CryptoExponent;
  ULONG                         TimeZoneId;
  ULONG                         LargePageMinimum;
  ULONG                         AitSamplingValue;
  ULONG                         AppCompatFlag;
  ULONGLONG                     RNGSeedVersion;
  ULONG                         GlobalValidationRunlevel;
  LONG                          TimeZoneBiasStamp;
  ULONG                         NtBuildNumber;
  NT_PRODUCT_TYPE               NtProductType;
  BOOLEAN                       ProductTypeIsValid;
  BOOLEAN                       Reserved0[1];
  USHORT                        NativeProcessorArchitecture;
  ULONG                         NtMajorVersion;
  ULONG                         NtMinorVersion;
  BOOLEAN                       ProcessorFeatures[PROCESSOR_FEATURE_MAX];
  ULONG                         Reserved1;
  ULONG                         Reserved3;
  ULONG                         TimeSlip;
  ALTERNATIVE_ARCHITECTURE_TYPE AlternativeArchitecture;
  ULONG                         BootId;
  LARGE_INTEGER                 SystemExpirationDate;
  ULONG                         SuiteMask;
  BOOLEAN                       KdDebuggerEnabled;
  union {
    UCHAR MitigationPolicies;
    struct {
      UCHAR NXSupportPolicy : 2;
      UCHAR SEHValidationPolicy : 2;
      UCHAR CurDirDevicesSkippedForDlls : 2;
      UCHAR Reserved : 2;
    };
  };
  USHORT                        CyclesPerYield;
  ULONG                         ActiveConsoleId;
  ULONG                         DismountCount;
  ULONG                         ComPlusPackage;
  ULONG                         LastSystemRITEventTickCount;
  ULONG                         NumberOfPhysicalPages;
  BOOLEAN                       SafeBootMode;
  union {
    UCHAR VirtualizationFlags;
    struct {
      UCHAR ArchStartedInEl2 : 1;
      UCHAR QcSlIsSupported : 1;
    };
  };
  UCHAR                         Reserved12[2];
  union {
    ULONG SharedDataFlags;
    struct {
      ULONG DbgErrorPortPresent : 1;
      ULONG DbgElevationEnabled : 1;
      ULONG DbgVirtEnabled : 1;
      ULONG DbgInstallerDetectEnabled : 1;
      ULONG DbgLkgEnabled : 1;
      ULONG DbgDynProcessorEnabled : 1;
      ULONG DbgConsoleBrokerEnabled : 1;
      ULONG DbgSecureBootEnabled : 1;
      ULONG DbgMultiSessionSku : 1;
      ULONG DbgMultiUsersInSessionSku : 1;
      ULONG DbgStateSeparationEnabled : 1;
      ULONG SpareBits : 21;
    } DUMMYSTRUCTNAME2;
  } DUMMYUNIONNAME2;
  ULONG                         DataFlagsPad[1];
  ULONGLONG                     TestRetInstruction;
  LONGLONG                      QpcFrequency;
  ULONG                         SystemCall;
  ULONG                         Reserved2;
  ULONGLONG                     FullNumberOfPhysicalPages;
  ULONGLONG                     SystemCallPad[1];
  union {
    KSYSTEM_TIME TickCount;
    ULONG64      TickCountQuad;
    struct {
      ULONG ReservedTickCountOverlay[3];
      ULONG TickCountPad[1];
    } DUMMYSTRUCTNAME;
  } DUMMYUNIONNAME3;
  ULONG                         Cookie;
  ULONG                         CookiePad[1];
  LONGLONG                      ConsoleSessionForegroundProcessId;
  ULONGLONG                     TimeUpdateLock;
  ULONGLONG                     BaselineSystemTimeQpc;
  ULONGLONG                     BaselineInterruptTimeQpc;
  ULONGLONG                     QpcSystemTimeIncrement;
  ULONGLONG                     QpcInterruptTimeIncrement;
  UCHAR                         QpcSystemTimeIncrementShift;
  UCHAR                         QpcInterruptTimeIncrementShift;
  USHORT                        UnparkedProcessorCount;
  ULONG                         EnclaveFeatureMask[4];
  ULONG                         TelemetryCoverageRound;
  USHORT                        UserModeGlobalLogger[16];
  ULONG                         ImageFileExecutionOptions;
  ULONG                         LangGenerationCount;
  ULONGLONG                     Reserved4;
  ULONGLONG                     InterruptTimeBias;
  ULONGLONG                     QpcBias;
  ULONG                         ActiveProcessorCount;
  UCHAR                         ActiveGroupCount;
  UCHAR                         Reserved9;
  union {
    USHORT QpcData;
    struct {
      UCHAR QpcBypassEnabled;
      UCHAR QpcReserved;
    };
  };
  LARGE_INTEGER                 TimeZoneBiasEffectiveStart;
  LARGE_INTEGER                 TimeZoneBiasEffectiveEnd;
  XSTATE_CONFIGURATION          XState;
  KSYSTEM_TIME                  FeatureConfigurationChangeStamp;
  ULONG                         Spare;
  ULONG64                       UserPointerAuthMask;
  XSTATE_CONFIGURATION          XStateArm64;
  ULONG                         Reserved10[210];
} KUSER_SHARED_DATA, *PKUSER_SHARED_DATA;

anyone understand the point I'm trying to get, please give any advice or any possible information, i know cheat engine is open source, but i don't know how to read such big projects... a lot of dirs and files with each one has over 2k line....I'm not on that level yet, pretty sure shrekushka ,varwar , DildoFagins would say something, I searched a lot, but nothing that explain that process..

thank you all))
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Don't really understand what exactly are not clear for you? You can build a set of valid memory regions with VirtualQuery, than pretty much walk the stack (NtQueryInformationThread) or heap (HeapWalk) looking for a pointers that targets your valid memory regions, most of the time I think you won't need to scan at each byte, but each 4/8-byte range, as a lot of structs in C/C++ are aligned.
 
Don't really understand what exactly are not clear for you? You can build a set of valid memory regions with VirtualQuery, than pretty much walk the stack (NtQueryInformationThread) or heap (HeapWalk) looking for a pointers that targets your valid memory regions, most of the time I think you won't need to scan at each byte, but each 4/8-byte range, as a lot of structs in C/C++ are aligned.
well let me explain, i don't know if you ever used cheat engine, but you know when u do scan for the whole memory and it's shows all pointers available on that virtual memory, including that ones on heap and stack, and there's another stuff called pointer scanning, which you get through offsets of a static pointer that is on the head , belong to the base address, this is what i'm trying to understand

and can you specify with a C++ code about what you talk about brother DildoFagins
 
but each 4/8-byte range, as a lot of structs in C/C++ are aligned.
do you mean also that most of the pointers are 4 bytes that can be int or float? that's why?
honestly my whole goal is to rewrite something like cheat engine, that will let me understand how memory work and so on, by the way, thank you for that info "walk the stack (NtQueryInformationThread) or heap (HeapWalk)" but i need a sample
 
The basic idea is to start from the final addr of the data (eg your in game penis size). Call that addr T. You want to find all ptrs (4byte/8byte vals) in the process that contain the val T. Such that
*A = T
Let’s call that set of addrs A = {A1, A2, ...}

Next, you want to find all addrs B in the process for which *B points to any of those in A.
i.e. *B = A1 or *B = A2...

You keep iterating “one level up” until you eventually reach:
1. a known static region (eg inside a loaded module's .data)
or
2. a region you consider "root enough" (eg PEB/TEB/ a module base)

Each time you go one level up, you record the “offset chain” from that higher level ptr to your final addr T. For instance, if *(B + offset) = T, you record that chain as [B]-> +offset -> T
The end result is a list of possible ptr paths from static or otherwise “stable” addrs down to the final target addr.

Because you do this “bottom up,” every ptr chain you follow definitely leads to the addr of interest. If you tried the “top down” approach: starting from every static addr or from the base of the module: most attempts would go nowhere or lead to a different piece of data. The amount of random paths you’d have to check would explode.


VirtualQuery to enum all committed pages -> check page protections -> ReadProcessMemory (ext) / direct reads (internal) on those pages to get their 4byte/8bytes -> search every aligned addr in those pages, blah blah
That's the scanning phase. You do it in multiple passes/“levels".

Those kernel structs (like KUSER_SHARED_DATA) are just additional stable anchors/sources of “interesting ptrs”. Often, the main ptr scanning loop is still about scanning usermode allocated mem (heap/stack/mem mapped files etc).

And regarding aligment:
64bit process: ptrs are 8bytes. 32bit: 4bytes
Scanning increments in those strides because:
1. a std C/C++ ptr is aligned to its natural ptr size.
2. if you scanned every single byte, it would be slower and 90% of those partial alignments are not valid ptr fields in typical structs.



see it like raytracing. Instead of going from a lightsource following every possible path until a screen pixel is hit, go from a screenpixel and follow the paths until a lightsource is detected
Cute.



PS.
Storing ptr “chains” something like [ModuleBase+0xXXXX] -> [Pointer+0xYY] -> T to keep track of each path discovered
 
The basic idea is to start from the final addr of the data (eg your in game penis size). Call that addr T. You want to find all ptrs (4byte/8byte vals) in the process that contain the val T. Such that
*A = T
Let’s call that set of addrs A = {A1, A2, ...}

Next, you want to find all addrs B in the process for which *B points to any of those in A.
i.e. *B = A1 or *B = A2...

You keep iterating “one level up” until you eventually reach:
1. a known static region (eg inside a loaded module's .data)
or
2. a region you consider "root enough" (eg PEB/TEB/ a module base)

Each time you go one level up, you record the “offset chain” from that higher level ptr to your final addr T. For instance, if *(B + offset) = T, you record that chain as [B]-> +offset -> T
The end result is a list of possible ptr paths from static or otherwise “stable” addrs down to the final target addr.

Because you do this “bottom up,” every ptr chain you follow definitely leads to the addr of interest. If you tried the “top down” approach: starting from every static addr or from the base of the module: most attempts would go nowhere or lead to a different piece of data. The amount of random paths you’d have to check would explode.


VirtualQuery to enum all committed pages -> check page protections -> ReadProcessMemory (ext) / direct reads (internal) on those pages to get their 4byte/8bytes -> search every aligned addr in those pages, blah blah
That's the scanning phase. You do it in multiple passes/“levels".

Those kernel structs (like KUSER_SHARED_DATA) are just additional stable anchors/sources of “interesting ptrs”. Often, the main ptr scanning loop is still about scanning usermode allocated mem (heap/stack/mem mapped files etc).

And regarding aligment:
64bit process: ptrs are 8bytes. 32bit: 4bytes
Scanning increments in those strides because:
1. a std C/C++ ptr is aligned to its natural ptr size.
2. if you scanned every single byte, it would be slower and 90% of those partial alignments are not valid ptr fields in typical structs.




Cute.



PS.
Storing ptr “chains” something like [ModuleBase+0xXXXX] -> [Pointer+0xYY] -> T to keep track of each path discovered
i see ,thank you , i totally understand the idea, but how the process should be? i meat how to find that set ? 2- how to check if u're on the data section for example ( the point we want to reach) , do you have samples ? please response on PM.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх