Hacking Homebrew Struggling of how use svcReadDebugProcessMemory()

davimassini

Member
Newcomer
Joined
Apr 27, 2025
Messages
17
Reaction score
63
Trophies
0
XP
207
Country
Brazil
Hey, I wonder how can I use svcReadDebugProcessMemory() (or dmntchtQueryCheatProcessMemory() (???))
See a lot of examples in GitHub, and all of them are kinda like mine but this just doesn't works.
I'll let some code bellow.

My log with the error (using svcReadDebugProcessMemory())
Code:
svcReadDebugProcessMemory failed: 0xe401
svcDebugActiveProcess failed: 0xf401

My log with the error (using dmntchtQueryCheatProcessMemory())
Code:
dmnt:cht is enabled
dmntchtForceOpenCheatProcess failed: 0xe401
No cheat process available.
Value: 0

From what I search, it means to be a permission error but I already parse my .json with many others and don't see anything wrong.
This build is a sys-module, I'll put below some codes and links that I used like ref.

Code using svcReadDebugProcessMemory()
C++:
bool read_process_memory(u64 pid, u64 address, void *buffer, size_t size) {
    if (!envIsSyscallHinted(0x6a)) {
        utils::write_log("Syscall hinting is not enabled.");
        return false;
    }

    Handle debug_handle;
    Result rc;

    rc = svcDebugActiveProcess(&debug_handle, pid);
    if (R_FAILED(rc)) {
        sprintf(log_message, "svcDebugActiveProcess failed: 0x%x\n", rc);
        utils::write_log(log_message);
        return false;
    }

    rc = svcReadDebugProcessMemory(buffer, pid, address, size);
    if (R_FAILED(rc)) {
        sprintf(log_message, "svcReadDebugProcessMemory failed: 0x%x", rc);
        utils::write_log(log_message);
        return false;
    }

    return true;
}

Code using dmntchtQueryCheatProcessMemory()
C++:
bool read_process_memory(u64 pid, u64 address, void *buffer, size_t size) {
    // if works, then I will use the pid to check if the process here is the same as I want

    utils::is_dmntcht_active();

    Result rc;
    rc = dmntchtForceOpenCheatProcess();
    if (R_FAILED(rc)) {
        sprintf(log_message, "dmntchtForceOpenCheatProcess failed: 0x%x", rc);
        utils::write_log(log_message);
    }

    bool has_cheat_process = false;
    rc = dmntchtHasCheatProcess(&has_cheat_process);
    if (R_FAILED(rc) || !has_cheat_process) {
        utils::write_log("No cheat process available.");
        return false;
    }

    MemoryInfo mem_info;
    rc = dmntchtQueryCheatProcessMemory(&mem_info, address);
    if (R_FAILED(rc) || address < mem_info.addr || (address + size) > (mem_info.addr + mem_info.size)) {
        utils::write_log("Address is invalid or outside a valid region.");
        return false;
    }

    rc = dmntchtReadCheatProcessMemory(address, buffer, size);
    if (R_FAILED(rc)) {
        sprintf(log_message, "dmntchtReadCheatProcessMemory failed: 0x%x", rc);
        utils::write_log(log_message);
        return false;
    }

    sprintf(log_message, "Memory read successful. RC: 0x%x", rc);
    utils::write_log(log_message);

    return true;
}

Method to verify if dmntcht is enable
C++:
void is_dmntcht_active() {
    Service dmntcht_service;
    Result rc = smGetService(&dmntcht_service, "dmnt:cht");
    if (R_SUCCEEDED(rc)) {
        write_log("dmnt:cht is enabled");
        serviceClose(&dmntcht_service);
    } else {
        snprintf(log_message, sizeof(log_message), "dmnt:cht failed: 0x%x\n", rc);
        write_log(log_message);
    }
}

Main.cpp __appInit
C++:
void __appInit(void) {
    Result rc;

    rc = smInitialize();
    if (R_FAILED(rc))
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM));

    rc = ldrDmntInitialize();
    if (R_FAILED(rc))
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized));

    rc = fsInitialize();
    if (R_FAILED(rc))
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_InitFail_FS));

    rc = hidInitialize();
    if (R_FAILED(rc))
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_InitFail_HID));

    rc = socketInitializeDefault();
    if (R_FAILED(rc))
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_NotInitialized));

    rc = pmdmntInitialize();
    if (R_FAILED(rc))
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_NotInitialized));

    rc = dmntchtInitialize();
    if (R_FAILED(rc))
        diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_NotInitialized));

    fsdevMountSdmc();
}


My .json (already tried changing all of debug_flags)
JSON:
{
    "name": "test-sysmodule",
    "title_id": "0x0100000000FFACDC",
    "title_id_range_min": "0x0100000000FFACDC",
    "title_id_range_max": "0x0100000000FFACDC",
    "main_thread_stack_size": "0x00004000",
    "main_thread_priority": 44,
    "default_cpu_id": 3,
    "process_category": 1,
    "is_retail": true,
    "pool_partition": 2,
    "is_64_bit": true,
    "address_space_type": 3,
    "capabilities": [
        "sysmodule",
        "applet",
        "network"
    ],
    "filesystem_access": {
        "permissions": "0xffffffffffffffff"
    },
    "service_access": [
        "*", "dmnt:cht"
    ],
    "service_host": [
        "*"
    ],
    "fs_access": {
        "permissions": "ro,sdmc"
    },
    "kernel_capabilities": [
        {
            "type": "kernel_flags",
            "value": {
                "highest_thread_priority": 63,
                "lowest_thread_priority": 24,
                "lowest_cpu_id": 3,
                "highest_cpu_id": 3
            }
        },
        {
            "type": "syscalls",
            "value": {
                "svcUnknown00": "0x00",
                "svcSetHeapSize": "0x01",
                "svcSetMemoryPermission": "0x02",
                "svcSetMemoryAttribute": "0x03",
                "svcMapMemory": "0x04",
                "svcUnmapMemory": "0x05",
                "svcQueryMemory": "0x06",
                "svcExitProcess": "0x07",
                "svcCreateThread": "0x08",
                "svcStartThread": "0x09",
                "svcExitThread": "0x0a",
                "svcSleepThread": "0x0b",
                "svcGetThreadPriority": "0x0c",
                "svcSetThreadPriority": "0x0d",
                "svcGetThreadCoreMask": "0x0e",
                "svcSetThreadCoreMask": "0x0f",
                "svcGetCurrentProcessorNumber": "0x10",
                "svcSignalEvent": "0x11",
                "svcClearEvent": "0x12",
                "svcMapSharedMemory": "0x13",
                "svcUnmapSharedMemory": "0x14",
                "svcCreateTransferMemory": "0x15",
                "svcCloseHandle": "0x16",
                "svcResetSignal": "0x17",
                "svcWaitSynchronization": "0x18",
                "svcCancelSynchronization": "0x19",
                "svcArbitrateLock": "0x1a",
                "svcArbitrateUnlock": "0x1b",
                "svcWaitProcessWideKeyAtomic": "0x1c",
                "svcSignalProcessWideKey": "0x1d",
                "svcGetSystemTick": "0x1e",
                "svcConnectToNamedPort": "0x1f",
                "svcSendSyncRequestLight": "0x20",
                "svcSendSyncRequest": "0x21",
                "svcSendSyncRequestWithUserBuffer": "0x22",
                "svcSendAsyncRequestWithUserBuffer": "0x23",
                "svcGetProcessId": "0x24",
                "svcGetThreadId": "0x25",
                "svcBreak": "0x26",
                "svcOutputDebugString": "0x27",
                "svcReturnFromException": "0x28",
                "svcGetInfo": "0x29",
                "svcFlushEntireDataCache": "0x2a",
                "svcFlushDataCache": "0x2b",
                "svcMapPhysicalMemory": "0x2c",
                "svcUnmapPhysicalMemory": "0x2d",
                "svcGetFutureThreadInfo": "0x2e",
                "svcGetLastThreadInfo": "0x2f",
                "svcGetResourceLimitLimitValue": "0x30",
                "svcGetResourceLimitCurrentValue": "0x31",
                "svcSetThreadActivity": "0x32",
                "svcGetThreadContext3": "0x33",
                "svcWaitForAddress": "0x34",
                "svcSignalToAddress": "0x35",
                "svcUnknown36": "0x36",
                "svcUnknown37": "0x37",
                "svcUnknown38": "0x38",
                "svcUnknown39": "0x39",
                "svcUnknown3a": "0x3a",
                "svcUnknown3b": "0x3b",
                "svcDumpInfo": "0x3c",
                "svcDumpInfoNew": "0x3d",
                "svcUnknown3e": "0x3e",
                "svcUnknown3f": "0x3f",
                "svcCreateSession": "0x40",
                "svcAcceptSession": "0x41",
                "svcReplyAndReceiveLight": "0x42",
                "svcReplyAndReceive": "0x43",
                "svcReplyAndReceiveWithUserBuffer": "0x44",
                "svcCreateEvent": "0x45",
                "svcUnknown46": "0x46",
                "svcUnknown47": "0x47",
                "svcMapPhysicalMemoryUnsafe": "0x48",
                "svcUnmapPhysicalMemoryUnsafe": "0x49",
                "svcSetUnsafeLimit": "0x4a",
                "svcCreateCodeMemory": "0x4b",
                "svcControlCodeMemory": "0x4c",
                "svcSleepSystem": "0x4d",
                "svcReadWriteRegister": "0x4e",
                "svcSetProcessActivity": "0x4f",
                "svcCreateSharedMemory": "0x50",
                "svcMapTransferMemory": "0x51",
                "svcUnmapTransferMemory": "0x52",
                "svcCreateInterruptEvent": "0x53",
                "svcQueryPhysicalAddress": "0x54",
                "svcQueryIoMapping": "0x55",
                "svcCreateDeviceAddressSpace": "0x56",
                "svcAttachDeviceAddressSpace": "0x57",
                "svcDetachDeviceAddressSpace": "0x58",
                "svcMapDeviceAddressSpaceByForce": "0x59",
                "svcMapDeviceAddressSpaceAligned": "0x5a",
                "svcMapDeviceAddressSpace": "0x5b",
                "svcUnmapDeviceAddressSpace": "0x5c",
                "svcInvalidateProcessDataCache": "0x5d",
                "svcStoreProcessDataCache": "0x5e",
                "svcFlushProcessDataCache": "0x5f",
                "svcDebugActiveProcess": "0x60",
                "svcBreakDebugProcess": "0x61",
                "svcTerminateDebugProcess": "0x62",
                "svcGetDebugEvent": "0x63",
                "svcContinueDebugEvent": "0x64",
                "svcGetProcessList": "0x65",
                "svcGetThreadList": "0x66",
                "svcGetDebugThreadContext": "0x67",
                "svcSetDebugThreadContext": "0x68",
                "svcQueryDebugProcessMemory": "0x69",
                "svcReadDebugProcessMemory": "0x6a",
                "svcWriteDebugProcessMemory": "0x6b",
                "svcSetHardwareBreakPoint": "0x6c",
                "svcGetDebugThreadParam": "0x6d",
                "svcUnknown": "0x6e",
                "svcGetSystemInfo": "0x6f",
                "svcCreatePort": "0x70",
                "svcManageNamedPort": "0x71",
                "svcConnectToPort": "0x72",
                "svcSetProcessMemoryPermission": "0x73",
                "svcMapProcessMemory": "0x74",
                "svcUnmapProcessMemory": "0x75",
                "svcQueryProcessMemory": "0x76",
                "svcMapProcessCodeMemory": "0x77",
                "svcUnmapProcessCodeMemory": "0x78",
                "svcCreateProcess": "0x79",
                "svcStartProcess": "0x7a",
                "svcTerminateProcess": "0x7b",
                "svcGetProcessInfo": "0x7c",
                "svcCreateResourceLimit": "0x7d",
                "svcSetResourceLimitLimitValue": "0x7e",
                "svcCallSecureMonitor": "0x7f"
            }
        },
        {
            "type": "min_kernel_version",
            "value": "0x0060"
        },
        {
            "type": "handle_table_size",
            "value": 1023
        },
        {
            "type": "debug_flags",
            "value": {
                "allow_debug": false,
                "force_debug": true,
                "force_debug_prod": false
            }
        }
    ]
}

Refs that I use (I can't post links)
Noexs - main.cpp
sys-netcheat - main.c
Noexs - noexs.json
sys-netcheat - sys-netcheat.json
Breeze-Beta - dmntcht.c
And a lot of other sources...

I really think that I'm losing something or misconcepting, if anyone can help I'll be grateful.
 
First, you cannot use svcDebug if dmnt:cht is active and there is "cheats" folder available for game you are running. dmnt:cht will activate even if nothing is in that folder.
Dunno if it's related.

You are initializing dmnt:cht twice - once with dmntchtInitialize, second time with smGetService. You should check if service exists before using dmntchtInitialize(), not after.
 
First, you cannot use svcDebug if dmnt:cht is active and there is "cheats" folder available for game you are running. dmnt:cht will activate even if nothing is in that folder.
Dunno if it's related.

You are initializing dmnt:cht twice - once with dmntchtInitialize, second time with smGetService. You should check if service exists before using dmntchtInitialize(), not after.
Before use dmnt:cht (that I don't know very well, my first time doing anything about Switch so I'm going with trial and error), I'm using only svcDebug() (even tried start the game with L pressed) but still getting the same error.
Because of Breeze, I thought the result of both methods will be likeable... But maybe I misconcept the library.

I'll try remove everything about dmnt:cht (even the files) from my module and do some tests... Thanks for the answer.
My concept is looking into some memory address and take the value, if you know some way to make this simple lmk xD
 
I'll try remove everything about dmnt:cht (even the files) from my module and do some tests... Thanks for the answer.
Just use dmnt:cht. It will be much easier for you to use.

Example of how I'm initializing code (omitting smInitialize since it's not needed in NRO)
C:
bool isServiceRunning(const char *serviceName) {   
    Handle handle;   
    SmServiceName service_name = smEncodeName(serviceName);   
    if (R_FAILED(smRegisterService(&handle, service_name, false, 1)))
        return true;
    else {
        svcCloseHandle(handle);   
        smUnregisterService(service_name);
        return false;
    }
}
[...]
    if (!isServiceRunning("dmnt:cht")) {
        printf("DMNT:CHT not detected!\n");
        error = true;
    }
[...]
        dmntchtInitialize();
        bool hasCheatProcess = false;
        dmntchtHasCheatProcess(&hasCheatProcess);
        if (!hasCheatProcess) {
            dmntchtForceOpenCheatProcess();
        }
And that's basically it. You don't need to force open cheat process if it's already opened.

And to make life easier: first try to write NRO with console output, when it will work then move it to sysmodule.
 
  • Like
Reactions: davimassini
Just use dmnt:cht. It will be much easier for you to use.

Example of how I'm initializing code (omitting smInitialize since it's not needed in NRO)
C:
bool isServiceRunning(const char *serviceName) {  
    Handle handle;  
    SmServiceName service_name = smEncodeName(serviceName);  
    if (R_FAILED(smRegisterService(&handle, service_name, false, 1)))
        return true;
    else {
        svcCloseHandle(handle);  
        smUnregisterService(service_name);
        return false;
    }
}
[...]
    if (!isServiceRunning("dmnt:cht")) {
        printf("DMNT:CHT not detected!\n");
        error = true;
    }
[...]
        dmntchtInitialize();
        bool hasCheatProcess = false;
        dmntchtHasCheatProcess(&hasCheatProcess);
        if (!hasCheatProcess) {
            dmntchtForceOpenCheatProcess();
        }
And that's basically it. You don't need to force open cheat process if it's already opened.

And to make life easier: first try to write NRO with console output, when it will work then move it to sysmodule.
Thanks a lot, I'll certainly give it a try and change my workflow.
Guess I overload my module and know is kinda difficult to get where the error are without the know-how... xD
 

Site & Scene News

Popular threads in this forum