The syscall table is read-only, and has been since kernel 2.6.16. However, a kernel rootkit has the ability to make it writable again. All it needs to do is execute a function like this* with the table as the argument:
static void set_addr_rw(const unsigned long addr)
unsigned int level;
pte = lookup_address(addr, &level);
if (pte->pte &~ _PAGE_RW)
pte->pte |= _PAGE_RW;
This changes the permissions of the syscall table and makes it possible to edit it. If this doesn't work for whatever reason, then write protection in the kernel can be globally disabled with the following ASM:
mov %cr0, %eax
and $~0x10000, %eax
mov %eax, %cr0
This disables interrupts, disables the WP (Write-Protect) bit in CR0, and re-enables interrupts.
So why is it marked as read-only if it's so easy to disable? One reason is that vulnerabilities exist which allow modifying kernel memory but not necessarily directly executing code. By marking critical areas of the kernel as read-only, it becomes more difficult to exploit them without finding an additional vulnerability to mark the pages as writable (or disable write-protection altogether). This doesn't provide very strong security, so the main reason that it is marked as read-only is to make it easier to catch any accidental overwrites caused by bugs from causing a catastrophic and unrecoverable system crash.
* The kernel's internal API changes all the time, so this exact function may not work on older kernels or newer kernels. Globally disabling
CR0.WP in ASM however is guaranteed to work on all x86 systems regardless of the kernel version.