Getting Started Welcome Setting Up Game Tree GuiRoot Method
Writing a Memory Library Reading and Writing Page Iterator Pattern Scanning Buffer Strategies
Writing a Roblox Library C++ String

GuiRoot Method

In this guide, we will find datamodel using the GuiRoot method

To find datamodel using guiroot method, you have to do 3 things:

  1. Find GuiRoot GuiItem string
  2. Offset that to find Fake Datamodel
  3. Get Datamodel from Fake Datamodel

Here is an example code of how to do Gui Root method:

const GuiRootMethod = struct {
    // Scan for GuiRoot GuiItem
    fn findGuiRootString(allocator: std.mem.Allocator, memory_accessor: anytype) !Memory.Address {
        // Create buffer to store memory read from Roblox
        var buffer: std.ArrayList(u8) = .{};
        // Free buffer once done
        defer buffer.deinit(allocator);
        // Create page iterator
        var it = Memory.PageIterator.init(memory_accessor);
        // Iterate through all the pages
        while (it.next()) |mbi| {
            // We only want readable and writable pages but not executable
            if(!mbi.canRead() or !mbi.canWrite() or mbi.canExecute())
                continue;
            // Resize buffer to match page size
            try buffer.resize(allocator, mbi.getRegionSize());
            // Read from roblox into our buffer
            memory_accessor.readSlice(mbi.getBaseAddress(), u8, buffer.items) catch continue;
            // Search for string "GuiRoot\x00GuiItem\x00" in the buffer and return the address
            return mbi.getBaseAddress().shiftBy(std.mem.indexOf(u8, buffer.items, "GuiRoot\x00GuiItem\x00") orelse continue);
        }
        // If not found, return nullptr
        return Memory.Address.nullptr();
    }
    pub fn getDatamodel(allocator: std.mem.Allocator, memory_accessor: anytype) !Roblox.Instance.View {
        // get root gui str address
        const guirootstr = try findGuiRootString(allocator, memory_accessor);
        if(guirootstr.isNull())
            return error.GuiRootStringNotFound;
        // get fake datamodel from that
        const fake_datamodel = try guirootstr.shiftBy(Roblox.Config.get("Offset/GuiRootMethod/FakeDatamodel")).read(memory_accessor, Memory.Address);
        // get datamodel from fake datamodel
        const datamodel = try fake_datamodel.shiftBy(Roblox.Config.get("Offset/FakeDatamodel/Datamodel")).read(memory_accessor, Memory.Address);
        return Roblox.Instance.View.of(datamodel);
    }
};

Now let's compare the result of GuiRoot method vs TopDown method

...
    // Print datamodel address
    try stdout.print("{s}{X}{c}", .{ "(TopDown) Datamodel is: ", datamodel.getPtr(), '\n' });
    // Gui Root method
    const guiroot_datamodel = try GuiRootMethod.getDatamodel(allocator, memory_accessor);
    try stdout.print("{s}{X}{c}", .{ "(GuiRoot) Datamodel is: ", guiroot_datamodel.getPtr(), '\n' });
...

We got:

Roblox process handle is: A8
(TopDown) Datamodel is: 21B018A3E38
(GuiRoot) Datamodel is: 21B018A3E38
Datamodel name is: Ugc

It works! Remember the offsets used here are available in config.hscl on this site as always.