mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-12 02:01:02 +01:00
dmnt: various cheat changes/suggestions that have been cooking a while
This commit is contained in:
parent
0c596e682f
commit
389c3b6baa
@ -19,6 +19,8 @@ This behavior ensures that cheat codes are only loaded when the user would want
|
|||||||
|
|
||||||
In cases where `dmnt` has not activated the cheat manager, but the user wants to make it do so anyway, the cheat manager's service API provides a `ForceOpenCheatProcess` command that homebrew can use. This command will cause the cheat manager to try to force itself to attach to the process.
|
In cases where `dmnt` has not activated the cheat manager, but the user wants to make it do so anyway, the cheat manager's service API provides a `ForceOpenCheatProcess` command that homebrew can use. This command will cause the cheat manager to try to force itself to attach to the process.
|
||||||
|
|
||||||
|
In cases where `dmnt` has activated the cheat manager, but the user wants to use an alternate debugger, the cheat manager's service API provides a `ForceCloseCheatProcess` command that homebrew can use. This command will cause the cheat manager to detach itself from the process.
|
||||||
|
|
||||||
By default, all cheat codes listed in the loaded .txt file will be toggled on. This is configurable by the user by editing the `atmosphere!dmnt_cheats_enabled_by_default` [system setting](configurations.md).
|
By default, all cheat codes listed in the loaded .txt file will be toggled on. This is configurable by the user by editing the `atmosphere!dmnt_cheats_enabled_by_default` [system setting](configurations.md).
|
||||||
|
|
||||||
Users may use homebrew programs to toggle cheats on and off at runtime via the cheat manager's service API.
|
Users may use homebrew programs to toggle cheats on and off at runtime via the cheat manager's service API.
|
||||||
@ -47,7 +49,7 @@ Code type 0x0 allows writing a static value to a memory address.
|
|||||||
`0TMR00AA AAAAAAAA VVVVVVVV (VVVVVVVV)`
|
`0TMR00AA AAAAAAAA VVVVVVVV (VVVVVVVV)`
|
||||||
|
|
||||||
+ T: Width of memory write (1, 2, 4, or 8 bytes).
|
+ T: Width of memory write (1, 2, 4, or 8 bytes).
|
||||||
+ M: Memory region to write to (0 = Main NSO, 1 = Heap).
|
+ M: Memory region to write to (0 = Main NSO, 1 = Heap, 2 = Alias, 3 = Aslr).
|
||||||
+ R: Register to use as an offset from memory region base.
|
+ R: Register to use as an offset from memory region base.
|
||||||
+ A: Immediate offset to use from memory region base.
|
+ A: Immediate offset to use from memory region base.
|
||||||
+ V: Value to write.
|
+ V: Value to write.
|
||||||
@ -63,7 +65,7 @@ If the condition is not met, all instructions until the appropriate conditional
|
|||||||
`1TMC00AA AAAAAAAA VVVVVVVV (VVVVVVVV)`
|
`1TMC00AA AAAAAAAA VVVVVVVV (VVVVVVVV)`
|
||||||
|
|
||||||
+ T: Width of memory write (1, 2, 4, or 8 bytes).
|
+ T: Width of memory write (1, 2, 4, or 8 bytes).
|
||||||
+ M: Memory region to write to (0 = Main NSO, 1 = Heap).
|
+ M: Memory region to write to (0 = Main NSO, 1 = Heap, 2 = Alias, 3 = Aslr).
|
||||||
+ C: Condition to use, see below.
|
+ C: Condition to use, see below.
|
||||||
+ A: Immediate offset to use from memory region base.
|
+ A: Immediate offset to use from memory region base.
|
||||||
+ V: Value to compare to.
|
+ V: Value to compare to.
|
||||||
@ -120,7 +122,7 @@ Code type 0x5 allows loading a value from memory into a register, either using a
|
|||||||
`5TMR00AA AAAAAAAA`
|
`5TMR00AA AAAAAAAA`
|
||||||
|
|
||||||
+ T: Width of memory read (1, 2, 4, or 8 bytes).
|
+ T: Width of memory read (1, 2, 4, or 8 bytes).
|
||||||
+ M: Memory region to write to (0 = Main NSO, 1 = Heap).
|
+ M: Memory region to write to (0 = Main NSO, 1 = Heap, 2 = Alias, 3 = Aslr).
|
||||||
+ R: Register to load value into.
|
+ R: Register to load value into.
|
||||||
+ A: Immediate offset to use from memory region base.
|
+ A: Immediate offset to use from memory region base.
|
||||||
|
|
||||||
|
@ -74,6 +74,10 @@ Result dmntchtResumeCheatProcess(void) {
|
|||||||
return _dmntchtCmdVoid(&g_dmntchtSrv, 65005);
|
return _dmntchtCmdVoid(&g_dmntchtSrv, 65005);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result dmntchtForceCloseCheatProcess(void) {
|
||||||
|
return _dmntchtCmdVoid(&g_dmntchtSrv, 65006);
|
||||||
|
}
|
||||||
|
|
||||||
static Result _dmntchtGetCount(u64 *out_count, u32 cmd_id) {
|
static Result _dmntchtGetCount(u64 *out_count, u32 cmd_id) {
|
||||||
return serviceDispatchOut(&g_dmntchtSrv, cmd_id, *out_count);
|
return serviceDispatchOut(&g_dmntchtSrv, cmd_id, *out_count);
|
||||||
}
|
}
|
||||||
@ -171,6 +175,13 @@ Result dmntchtResetStaticRegisters() {
|
|||||||
return _dmntchtCmdVoid(&g_dmntchtSrv, 65208);
|
return _dmntchtCmdVoid(&g_dmntchtSrv, 65208);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result dmntchtSetMasterCheat(DmntCheatDefinition *cheat_def) {
|
||||||
|
return serviceDispatch(&g_dmntchtSrv, 65209,
|
||||||
|
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias | SfBufferAttr_FixedSize },
|
||||||
|
.buffers = { { cheat_def, sizeof(*cheat_def) } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Result dmntchtGetFrozenAddressCount(u64 *out_count) {
|
Result dmntchtGetFrozenAddressCount(u64 *out_count) {
|
||||||
return _dmntchtGetCount(out_count, 65300);
|
return _dmntchtGetCount(out_count, 65300);
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ Result dmntchtHasCheatProcess(bool *out);
|
|||||||
Result dmntchtGetCheatProcessEvent(Event *event);
|
Result dmntchtGetCheatProcessEvent(Event *event);
|
||||||
Result dmntchtGetCheatProcessMetadata(DmntCheatProcessMetadata *out_metadata);
|
Result dmntchtGetCheatProcessMetadata(DmntCheatProcessMetadata *out_metadata);
|
||||||
Result dmntchtForceOpenCheatProcess(void);
|
Result dmntchtForceOpenCheatProcess(void);
|
||||||
|
Result dmntchtForceCloseCheatProcess(void);
|
||||||
|
|
||||||
Result dmntchtGetCheatProcessMappingCount(u64 *out_count);
|
Result dmntchtGetCheatProcessMappingCount(u64 *out_count);
|
||||||
Result dmntchtGetCheatProcessMappings(MemoryInfo *buffer, u64 max_count, u64 offset, u64 *out_count);
|
Result dmntchtGetCheatProcessMappings(MemoryInfo *buffer, u64 max_count, u64 offset, u64 *out_count);
|
||||||
@ -84,6 +85,7 @@ Result dmntchtRemoveCheat(u32 cheat_id);
|
|||||||
Result dmntchtReadStaticRegister(u64 *out, u8 which);
|
Result dmntchtReadStaticRegister(u64 *out, u8 which);
|
||||||
Result dmntchtWriteStaticRegister(u8 which, u64 value);
|
Result dmntchtWriteStaticRegister(u8 which, u64 value);
|
||||||
Result dmntchtResetStaticRegisters();
|
Result dmntchtResetStaticRegisters();
|
||||||
|
Result dmntchtSetMasterCheat(DmntCheatDefinition *cheat);
|
||||||
|
|
||||||
Result dmntchtGetFrozenAddressCount(u64 *out_count);
|
Result dmntchtGetFrozenAddressCount(u64 *out_count);
|
||||||
Result dmntchtGetFrozenAddresses(DmntFrozenAddressEntry *buffer, u64 max_count, u64 offset, u64 *out_count);
|
Result dmntchtGetFrozenAddresses(DmntFrozenAddressEntry *buffer, u64 max_count, u64 offset, u64 *out_count);
|
||||||
|
@ -48,6 +48,10 @@ namespace ams::dmnt::cheat {
|
|||||||
return dmnt::cheat::impl::ResumeCheatProcess();
|
return dmnt::cheat::impl::ResumeCheatProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result CheatService::ForceCloseCheatProcess() {
|
||||||
|
return dmnt::cheat::impl::ForceCloseCheatProcess();
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================================================================================= */
|
/* ========================================================================================= */
|
||||||
/* =================================== Memory Commands =================================== */
|
/* =================================== Memory Commands =================================== */
|
||||||
/* ========================================================================================= */
|
/* ========================================================================================= */
|
||||||
@ -116,6 +120,10 @@ namespace ams::dmnt::cheat {
|
|||||||
return dmnt::cheat::impl::ResetStaticRegisters();
|
return dmnt::cheat::impl::ResetStaticRegisters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result CheatService::SetMasterCheat(const CheatDefinition &cheat) {
|
||||||
|
return dmnt::cheat::impl::SetMasterCheat(cheat);
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================================================================================= */
|
/* ========================================================================================= */
|
||||||
/* =================================== Address Commands ================================== */
|
/* =================================== Address Commands ================================== */
|
||||||
/* ========================================================================================= */
|
/* ========================================================================================= */
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
AMS_SF_METHOD_INFO(C, H, 65003, Result, ForceOpenCheatProcess, (), ()) \
|
AMS_SF_METHOD_INFO(C, H, 65003, Result, ForceOpenCheatProcess, (), ()) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65004, Result, PauseCheatProcess, (), ()) \
|
AMS_SF_METHOD_INFO(C, H, 65004, Result, PauseCheatProcess, (), ()) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65005, Result, ResumeCheatProcess, (), ()) \
|
AMS_SF_METHOD_INFO(C, H, 65005, Result, ResumeCheatProcess, (), ()) \
|
||||||
|
AMS_SF_METHOD_INFO(C, H, 65006, Result, ForceCloseCheatProcess, (), ()) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65100, Result, GetCheatProcessMappingCount, (sf::Out<u64> out_count), (out_count)) \
|
AMS_SF_METHOD_INFO(C, H, 65100, Result, GetCheatProcessMappingCount, (sf::Out<u64> out_count), (out_count)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65101, Result, GetCheatProcessMappings, (const sf::OutArray<MemoryInfo> &mappings, sf::Out<u64> out_count, u64 offset), (mappings, out_count, offset)) \
|
AMS_SF_METHOD_INFO(C, H, 65101, Result, GetCheatProcessMappings, (const sf::OutArray<MemoryInfo> &mappings, sf::Out<u64> out_count, u64 offset), (mappings, out_count, offset)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65102, Result, ReadCheatProcessMemory, (const sf::OutBuffer &buffer, u64 address, u64 out_size), (buffer, address, out_size)) \
|
AMS_SF_METHOD_INFO(C, H, 65102, Result, ReadCheatProcessMemory, (const sf::OutBuffer &buffer, u64 address, u64 out_size), (buffer, address, out_size)) \
|
||||||
@ -38,6 +39,7 @@
|
|||||||
AMS_SF_METHOD_INFO(C, H, 65206, Result, ReadStaticRegister, (sf::Out<u64> out, u8 which), (out, which)) \
|
AMS_SF_METHOD_INFO(C, H, 65206, Result, ReadStaticRegister, (sf::Out<u64> out, u8 which), (out, which)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65207, Result, WriteStaticRegister, (u8 which, u64 value), (which, value)) \
|
AMS_SF_METHOD_INFO(C, H, 65207, Result, WriteStaticRegister, (u8 which, u64 value), (which, value)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65208, Result, ResetStaticRegisters, (), ()) \
|
AMS_SF_METHOD_INFO(C, H, 65208, Result, ResetStaticRegisters, (), ()) \
|
||||||
|
AMS_SF_METHOD_INFO(C, H, 65209, Result, SetMasterCheat, (const dmnt::cheat::CheatDefinition &cheat), (cheat)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65300, Result, GetFrozenAddressCount, (sf::Out<u64> out_count), (out_count)) \
|
AMS_SF_METHOD_INFO(C, H, 65300, Result, GetFrozenAddressCount, (sf::Out<u64> out_count), (out_count)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65301, Result, GetFrozenAddresses, (const sf::OutArray<dmnt::cheat::FrozenAddressEntry> &addresses, sf::Out<u64> out_count, u64 offset), (addresses, out_count, offset)) \
|
AMS_SF_METHOD_INFO(C, H, 65301, Result, GetFrozenAddresses, (const sf::OutArray<dmnt::cheat::FrozenAddressEntry> &addresses, sf::Out<u64> out_count, u64 offset), (addresses, out_count, offset)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65302, Result, GetFrozenAddress, (sf::Out<dmnt::cheat::FrozenAddressEntry> entry, u64 address), (entry, address)) \
|
AMS_SF_METHOD_INFO(C, H, 65302, Result, GetFrozenAddress, (sf::Out<dmnt::cheat::FrozenAddressEntry> entry, u64 address), (entry, address)) \
|
||||||
@ -56,6 +58,7 @@ namespace ams::dmnt::cheat {
|
|||||||
Result ForceOpenCheatProcess();
|
Result ForceOpenCheatProcess();
|
||||||
Result PauseCheatProcess();
|
Result PauseCheatProcess();
|
||||||
Result ResumeCheatProcess();
|
Result ResumeCheatProcess();
|
||||||
|
Result ForceCloseCheatProcess();
|
||||||
|
|
||||||
Result GetCheatProcessMappingCount(sf::Out<u64> out_count);
|
Result GetCheatProcessMappingCount(sf::Out<u64> out_count);
|
||||||
Result GetCheatProcessMappings(const sf::OutArray<MemoryInfo> &mappings, sf::Out<u64> out_count, u64 offset);
|
Result GetCheatProcessMappings(const sf::OutArray<MemoryInfo> &mappings, sf::Out<u64> out_count, u64 offset);
|
||||||
@ -72,6 +75,7 @@ namespace ams::dmnt::cheat {
|
|||||||
Result ReadStaticRegister(sf::Out<u64> out, u8 which);
|
Result ReadStaticRegister(sf::Out<u64> out, u8 which);
|
||||||
Result WriteStaticRegister(u8 which, u64 value);
|
Result WriteStaticRegister(u8 which, u64 value);
|
||||||
Result ResetStaticRegisters();
|
Result ResetStaticRegisters();
|
||||||
|
Result SetMasterCheat(const CheatDefinition &cheat);
|
||||||
|
|
||||||
Result GetFrozenAddressCount(sf::Out<u64> out_count);
|
Result GetFrozenAddressCount(sf::Out<u64> out_count);
|
||||||
Result GetFrozenAddresses(const sf::OutArray<FrozenAddressEntry> &addresses, sf::Out<u64> out_count, u64 offset);
|
Result GetFrozenAddresses(const sf::OutArray<FrozenAddressEntry> &addresses, sf::Out<u64> out_count, u64 offset);
|
||||||
|
@ -301,6 +301,11 @@ namespace ams::dmnt::cheat::impl {
|
|||||||
return this->AttachToApplicationProcess(false);
|
return this->AttachToApplicationProcess(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ForceCloseCheatProcess() {
|
||||||
|
this->CloseActiveCheatProcess();
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
Result ReadCheatProcessMemoryUnsafe(u64 proc_addr, void *out_data, size_t size) {
|
Result ReadCheatProcessMemoryUnsafe(u64 proc_addr, void *out_data, size_t size) {
|
||||||
return svcReadDebugProcessMemory(out_data, this->GetCheatProcessHandle(), proc_addr, size);
|
return svcReadDebugProcessMemory(out_data, this->GetCheatProcessHandle(), proc_addr, size);
|
||||||
}
|
}
|
||||||
@ -520,6 +525,9 @@ namespace ams::dmnt::cheat::impl {
|
|||||||
/* Trigger a VM reload. */
|
/* Trigger a VM reload. */
|
||||||
this->SetNeedsReloadVm(true);
|
this->SetNeedsReloadVm(true);
|
||||||
|
|
||||||
|
/* Set output id. */
|
||||||
|
*out_id = new_entry->cheat_id;
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,6 +545,25 @@ namespace ams::dmnt::cheat::impl {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result SetMasterCheat(const CheatDefinition &def) {
|
||||||
|
std::scoped_lock lk(this->cheat_lock);
|
||||||
|
|
||||||
|
R_TRY(this->EnsureCheatProcess());
|
||||||
|
|
||||||
|
R_UNLESS(def.num_opcodes != 0, ResultCheatInvalid());
|
||||||
|
R_UNLESS(def.num_opcodes <= util::size(def.opcodes), ResultCheatInvalid());
|
||||||
|
|
||||||
|
CheatEntry *master_entry = this->cheat_entries + 0;
|
||||||
|
|
||||||
|
master_entry->enabled = true;
|
||||||
|
master_entry->definition = def;
|
||||||
|
|
||||||
|
/* Trigger a VM reload. */
|
||||||
|
this->SetNeedsReloadVm(true);
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
Result ReadStaticRegister(u64 *out, size_t which) {
|
Result ReadStaticRegister(u64 *out, size_t which) {
|
||||||
std::scoped_lock lk(this->cheat_lock);
|
std::scoped_lock lk(this->cheat_lock);
|
||||||
|
|
||||||
@ -1187,6 +1214,10 @@ namespace ams::dmnt::cheat::impl {
|
|||||||
return GetReference(g_cheat_process_manager).ResumeCheatProcess();
|
return GetReference(g_cheat_process_manager).ResumeCheatProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ForceCloseCheatProcess() {
|
||||||
|
return GetReference(g_cheat_process_manager).ForceCloseCheatProcess();
|
||||||
|
}
|
||||||
|
|
||||||
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, void *out_data, size_t size) {
|
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, void *out_data, size_t size) {
|
||||||
return GetReference(g_cheat_process_manager).ReadCheatProcessMemoryUnsafe(process_addr, out_data, size);
|
return GetReference(g_cheat_process_manager).ReadCheatProcessMemoryUnsafe(process_addr, out_data, size);
|
||||||
}
|
}
|
||||||
@ -1247,6 +1278,10 @@ namespace ams::dmnt::cheat::impl {
|
|||||||
return GetReference(g_cheat_process_manager).RemoveCheat(cheat_id);
|
return GetReference(g_cheat_process_manager).RemoveCheat(cheat_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result SetMasterCheat(const CheatDefinition &def) {
|
||||||
|
return GetReference(g_cheat_process_manager).SetMasterCheat(def);
|
||||||
|
}
|
||||||
|
|
||||||
Result ReadStaticRegister(u64 *out, size_t which) {
|
Result ReadStaticRegister(u64 *out, size_t which) {
|
||||||
return GetReference(g_cheat_process_manager).ReadStaticRegister(out, which);
|
return GetReference(g_cheat_process_manager).ReadStaticRegister(out, which);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ namespace ams::dmnt::cheat::impl {
|
|||||||
Result ForceOpenCheatProcess();
|
Result ForceOpenCheatProcess();
|
||||||
Result PauseCheatProcess();
|
Result PauseCheatProcess();
|
||||||
Result ResumeCheatProcess();
|
Result ResumeCheatProcess();
|
||||||
|
Result ForceCloseCheatProcess();
|
||||||
|
|
||||||
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, void *out_data, size_t size);
|
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, void *out_data, size_t size);
|
||||||
Result WriteCheatProcessMemoryUnsafe(u64 process_addr, void *data, size_t size);
|
Result WriteCheatProcessMemoryUnsafe(u64 process_addr, void *data, size_t size);
|
||||||
@ -45,6 +46,7 @@ namespace ams::dmnt::cheat::impl {
|
|||||||
Result ToggleCheat(u32 cheat_id);
|
Result ToggleCheat(u32 cheat_id);
|
||||||
Result AddCheat(u32 *out_id, const CheatDefinition &def, bool enabled);
|
Result AddCheat(u32 *out_id, const CheatDefinition &def, bool enabled);
|
||||||
Result RemoveCheat(u32 cheat_id);
|
Result RemoveCheat(u32 cheat_id);
|
||||||
|
Result SetMasterCheat(const CheatDefinition &def);
|
||||||
Result ReadStaticRegister(u64 *out, size_t which);
|
Result ReadStaticRegister(u64 *out, size_t which);
|
||||||
Result WriteStaticRegister(size_t which, u64 value);
|
Result WriteStaticRegister(size_t which, u64 value);
|
||||||
Result ResetStaticRegisters();
|
Result ResetStaticRegisters();
|
||||||
|
@ -721,6 +721,10 @@ namespace ams::dmnt::cheat::impl {
|
|||||||
return metadata->main_nso_extents.base + rel_address;
|
return metadata->main_nso_extents.base + rel_address;
|
||||||
case MemoryAccessType_Heap:
|
case MemoryAccessType_Heap:
|
||||||
return metadata->heap_extents.base + rel_address;
|
return metadata->heap_extents.base + rel_address;
|
||||||
|
case MemoryAccessType_Alias:
|
||||||
|
return metadata->alias_extents.base + rel_address;
|
||||||
|
case MemoryAccessType_Aslr:
|
||||||
|
return metadata->aslr_extents.base + rel_address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,9 @@ namespace ams::dmnt::cheat::impl {
|
|||||||
|
|
||||||
enum MemoryAccessType : u32 {
|
enum MemoryAccessType : u32 {
|
||||||
MemoryAccessType_MainNso = 0,
|
MemoryAccessType_MainNso = 0,
|
||||||
MemoryAccessType_Heap = 1,
|
MemoryAccessType_Heap = 1,
|
||||||
|
MemoryAccessType_Alias = 2,
|
||||||
|
MemoryAccessType_Aslr = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ConditionalComparisonType : u32 {
|
enum ConditionalComparisonType : u32 {
|
||||||
|
Loading…
Reference in New Issue
Block a user