[MOD][KERNEL MODULE] wp_mod: disable system write protection

Search This thread

graffixnyc

Retired Forum Mod / Inactive Recognized Developer
Jan 21, 2011
6,627
6,486
New York City
www.graffixnyc.com
Can you share your zip please

Sent from my HTC One_M8 using Tapatalk

I didn't use a zip.. I did make a guide though..

You need to run it each time you boot, until it is correctly working as init.


I made a guide for people on stock roms or roms that don't have init.d support that will enable r/w on boot without running the command every time you boot.

http://xdaforums.com/showthread.php?p=51560810
 

MassStash

Senior Member
Sep 26, 2008
3,838
2,446
36
Chicago
Same here, both this and s2w mod are working great on m8whl! I added this to lib/modules as s2w is, and then added correlating insmod lines to intall-revocery.sh, and they load perfectly each boot

Edit: Ah nice tut, that's exactly what I figured out this morning haha. Now that I can change dpi tho, need to edit prism grid size....

~m8whl modded stock
2.265 Max ¦ 300 Min
ondemand
row
 

m03sizlak

Senior Member
Jun 28, 2010
452
86
Buffalo, NY
Source:
wp_mod.c
Module was compiled against m8 Google Play Edition source. Some symbol CRC checks had to be hexedited in the compiled module to match the stock kernel. Thanks to Michael Coppola for example of function hooking on arm: http://poppopret.org/2013/01/07/suterusu-rootkit-inline-kernel-function-hooking-on-x86-and-arm/#arm

AWESOME work flar2.

After examining the source, it is indeed *much* more complicated than it has been in the past. Just curious, if you have the kernel source, what is to stop you from just rewriting the hooked functions instead of hijacking them with this code, which appears to be proof of concept code for ARM rootkits?

Second question, the very informative page you linked to, and based this on, says this about ARM instruction caching:

http://poppopret.org/2013/01/07/suterusu-rootkit-inline-kernel-function-hooking-on-x86-and-arm/#arm
Instruction Caching on ARM

Most Android devices do not enforce read-only kernel page permissions, so at least for now we can forego any potential voodoo magic to write to protected memory regions. It is still necessary, however, to consider the concept of instruction caching on ARM when performing a function hook.

ARM CPUs utilize a data cache and instruction cache for performance benefits. However, modifying code in-place may cause the instruction cache to become incoherent with the actual instructions in memory. According to the official ARM technical reference, this issue becomes readily apparent when developing self-modifying code. The solution is to simply flush the instruction cache whenever a modification to kernel text is made, which is accomplished by a call to the kernel routine flush_icache_range():
void cacheflush ( void *begin, unsigned long size )
{
flush_icache_range((unsigned long)begin, (unsigned long)begin + size);
}

Any reason why you do not use this approach in your module?


Last and possibly most important question. The page also says this:
Most rootkits traditionally perform system call hooking by swapping out function pointers in the system call table, but this technique is well known and trivially detectable by intelligent rootkit detectors. Instead of pursuing this route, Suterusu utilizes a different technique and performs hooking by modifying the prologue of the target function to transfer execution to the replacement routine. This can be observed by examining the following four functions:

hijack_start()
hijack_pause()
hijack_resume()
hijack_stop()
The code uses this approach only to avoid detection by rootkit detectors, something that we should have zero concerns about. Why not use the other approach, system call hooking by swapping out function pointers in the system call table?


THANK YOU.
 
Last edited:

flar2

Recognized Developer
Jun 11, 2012
18,897
87,868
Southwestern Ontario
elementalx.org
AWESOME work flar2.

After examining the source, it is indeed *much* more complicated than it has been in the past. Just curious, if you have the kernel source, what is to stop you from just rewriting the hooked functions instead of hijacking them with this code, which appears to be proof of concept code for ARM rootkits?

Second question, the very informative page you linked to, and based this on, says this about ARM instruction caching:

http://poppopret.org/2013/01/07/suterusu-rootkit-inline-kernel-function-hooking-on-x86-and-arm/#arm


Any reason why you do not use this approach in your module?


Last and possibly most important question. The page also says this:

The code uses this approach only to avoid detection by rootkit detectors, something that we should have zero concerns about. Why not use the other approach, system call hooking by swapping out function pointers in the system call table?


THANK YOU.

I did rewrite the function. Remember, we have to do this in the running kernel. Whenever the original function is called, it jumps to my new function instead. Hooking/hijacking are the same thing. That site also shows how to hide the module and a bunch of other stealth stuff, but none of that was necessary for this.

It's extremely easy to disable write protection if you compile your own kernel, you just turn off MMC_MUST_PREVENT_WP_VIOLATION.

Previously, the wp_mod hack was dead simple. All we had to do was call an existing kernel function to change the number of the partition that write protection applied to. In the new source (below), HTC got rid of all this extraneous code and just hardcoded it to apply the write protection to /system. This happens in block/blk-core.c as you can see below. We need to skip over the quoted code.

Code:
static noinline_for_stack bool
generic_make_request_checks(struct bio *bio)
{

......

#ifdef CONFIG_MMC_MUST_PREVENT_WP_VIOLATION
	sprintf(wp_ptn, "mmcblk0p%d", get_partition_num_by_name("system"));   //hardcoded to look for system partition
	if (!strcmp(bdevname(bio->bi_bdev, b), wp_ptn) && !board_mfg_mode() &&   //wp_ptn == mmcblk0p45  (/system)
			(get_tamper_sf() == 1) && (bio->bi_rw & WRITE)) {
		pr_info("blk-core: Attempt to write protected partition %s block %Lu \n",
				bdevname(bio->bi_bdev, b), (unsigned long long)bio->bi_sector);
		err = 0;
		goto wp_end_io;
	} else if (atomic_read(&emmc_reboot) && (bio->bi_rw & WRITE)) {
		pr_info("%s: Attempt to write eMMC, %s block %Lu \n", current->comm,
				bdevname(bio->bi_bdev, b), (unsigned long long)bio->bi_sector);
		err = -EROFS;
		goto wp_end_io;
	}
#endif

..............

}


It's a *bad idea* to replace a big complicated important function like static noinline_for_stack bool
generic_make_request_checks() so I decided to modify a simpler function within it, get_partition_num_by_name(). I changed get_partition_num_by_name() to return a different partition number when name == system. I didn't see any code in the kernel source where it would cause a problem to return the wrong partition number for system. After loading wp_mod.ko, write protection is applied to a non-existent partition instead of /system. The end result is exactly the same as my old wp_mod that has proven to work on many devices.


Why didn't I just change the address in the system call table? I don't think that is so easy on contemporary kernels. I found the function hooking method simpler and more foolproof.


EDIT: in my haste while answering this at work, I quoted the wrong function containing the write protection code. It's static noinline_for_stack bool
generic_make_request_checks not bio_check_eod (which is the function right above it in blk-core.c)
 
Last edited:

flar2

Recognized Developer
Jun 11, 2012
18,897
87,868
Southwestern Ontario
elementalx.org
AWESOME work flar2.

After examining the source, it is indeed *much* more complicated than it has been in the past. Just curious, if you have the kernel source, what is to stop you from just rewriting the hooked functions instead of hijacking them with this code, which appears to be proof of concept code for ARM rootkits?

Second question, the very informative page you linked to, and based this on, says this about ARM instruction caching:

http://poppopret.org/2013/01/07/suterusu-rootkit-inline-kernel-function-hooking-on-x86-and-arm/#arm


Any reason why you do not use this approach in your module?


Last and possibly most important question. The page also says this:

The code uses this approach only to avoid detection by rootkit detectors, something that we should have zero concerns about. Why not use the other approach, system call hooking by swapping out function pointers in the system call table?


THANK YOU.

Further to your concerns about instruction caching and potential corruption, this commit addresses our situation, where CONFIG_STRICT_MEMORY_RWX is enabled: https://github.com/mncoppola/suterusu/commit/26f1361e3aa97bdf254ec8ee37efb5e58e941b80

mem_text_write_kernel_word() handles the cache flush.



I know a couple people have experience data corruption, but it is unclear if it is due to the module or not. If this happens, I would appreciate /proc/last_kmsg.

One thing I know causes corruption is attempting to make a change to /system before loading the module. This has always been so, it happened to me the when I first made the module back on the HTC One XL. I didn't try it here, just assume that it would happen.
 
Last edited:

m03sizlak

Senior Member
Jun 28, 2010
452
86
Buffalo, NY
I did rewrite the function. Remember, we have to do this in the running kernel. Whenever the original function is called, it jumps to my new function instead. Hooking/hijacking are the same thing. That site also shows how to hide the module and a bunch of other stealth stuff, but none of that was necessary for this.

It's extremely easy to disable write protection if you compile your own kernel, you just turn off MMC_MUST_PREVENT_WP_VIOLATION.

Previously, the wp_mod hack was dead simple. All we had to do was call an existing kernel function to change the number of the partition that write protection applied to. In the new source (below), HTC got rid of all this extraneous code and just hardcoded it to apply the write protection to /system. This happens in block/blk-core.c as you can see below. We need to skip over the quoted code.

It's a *bad idea* to replace a big complicated important function like bio_check_eod() so I decided to modify a simpler function within it, get_partition_num_by_name(). I changed get_partition_num_by_name() to return a different partition number when name == system. I didn't see any code in the kernel source where it would cause a problem to return the wrong partition number for system. After loading wp_mod.ko, write protection is applied to a non-existent partition instead of /system. The end result is exactly the same as my old wp_mod that has proven to work on many devices.


Why didn't I just change the address in the system call table? I don't think that is so easy on contemporary kernels. I found the function hooking method simpler and more foolproof.

flar2, thanks for the explanation. You have mostly assuaged my concerns. If neither bio_check_eod() nor get_partition_num_by_name() is a syscall, which I assume is true, then there would be no way to hook them cleanly via the system call table.

I also see what you mean about mem_text_write_kernel_word() handling the cache flush.

I guess my only remaining concern would be that you are returning this meaningless number when get_partition_num_by_name is called on the "system" partition. I'm not sure how easy it would be to determine exactly what kind of unintended consequences this could have. I assume you have searched all the other kernel code for places this is called, but still.

I would suggest trying to use a similar method to patch the existing bio_check_eod() function.

Code:
#ifdef CONFIG_MMC_MUST_PREVENT_WP_VIOLATION
	sprintf(wp_ptn, "mmcblk0p%d", get_partition_num_by_name("system"));   //hardcoded to look for system partition
	if (!strcmp(bdevname(bio->bi_bdev, b), wp_ptn) && !board_mfg_mode() &&   //wp_ptn == mmcblk0p45  (/system)
			(get_tamper_sf() == 1) && (bio->bi_rw & WRITE)) {
		pr_info("blk-core: Attempt to write protected partition %s block %Lu \n",
				bdevname(bio->bi_bdev, b), (unsigned long long)bio->bi_sector);
		err = 0;
		goto wp_end_io;
	} else if (atomic_read(&emmc_reboot) && (bio->bi_rw & WRITE)) {
		pr_info("%s: Attempt to write eMMC, %s block %Lu \n", current->comm,
				bdevname(bio->bi_bdev, b), (unsigned long long)bio->bi_sector);
		err = -EROFS;
		goto wp_end_io;
	}
#endif


You could try to replace that entire first if-block with NOP's (0x00000000). Or if you can find where the "mmcblk0p%d" string literal is allocated and replace it with "mXXXXk0p%d" or something.

Or as a last resort you could modify your replacement get_partition_num_by_name() function to only return the meaningless 666 value for 'system' if the calling function is bio_check_eod()?
 
  • Like
Reactions: gjlowe and joeykrim

flar2

Recognized Developer
Jun 11, 2012
18,897
87,868
Southwestern Ontario
elementalx.org
flar2, thanks for the explanation. You have mostly assuaged my concerns. If neither bio_check_eod() nor get_partition_num_by_name() is a syscall, which I assume is true, then there would be no way to hook them cleanly via the system call table.

I also see what you mean about mem_text_write_kernel_word() handling the cache flush.

I guess my only remaining concern would be that you are returning this meaningless number when get_partition_num_by_name is called on the "system" partition. I'm not sure how easy it would be to determine exactly what kind of unintended consequences this could have. I assume you have searched all the other kernel code for places this is called, but still.

I would suggest trying to use a similar method to patch the existing bio_check_eod() function.

Code:
#ifdef CONFIG_MMC_MUST_PREVENT_WP_VIOLATION
	sprintf(wp_ptn, "mmcblk0p%d", get_partition_num_by_name("system"));   //hardcoded to look for system partition
	if (!strcmp(bdevname(bio->bi_bdev, b), wp_ptn) && !board_mfg_mode() &&   //wp_ptn == mmcblk0p45  (/system)
			(get_tamper_sf() == 1) && (bio->bi_rw & WRITE)) {
		pr_info("blk-core: Attempt to write protected partition %s block %Lu \n",
				bdevname(bio->bi_bdev, b), (unsigned long long)bio->bi_sector);
		err = 0;
		goto wp_end_io;
	} else if (atomic_read(&emmc_reboot) && (bio->bi_rw & WRITE)) {
		pr_info("%s: Attempt to write eMMC, %s block %Lu \n", current->comm,
				bdevname(bio->bi_bdev, b), (unsigned long long)bio->bi_sector);
		err = -EROFS;
		goto wp_end_io;
	}
#endif


You could try to replace that entire first if-block with NOP's (0x00000000). Or if you can find where the "mmcblk0p%d" string literal is allocated and replace it with "mXXXXk0p%d" or something.

Or as a last resort you could modify your replacement get_partition_num_by_name() function to only return the meaningless 666 value for 'system' if the calling function is bio_check_eod()?


I think finding the exact location of the if statement or string would be tough, given that this has to work on multiple kernel versions. We have to stick to addresses that can be found with kallsysms_lookup_name.

I was already thinking about doing your second suggestion. I've searched through the code and I don't think there are any circumstances that would call get_partition_num_by_name("system"), but future kernel changes and other devices may be different. For now, this seems to be the only situation that will return 666 and the only consequence will be that the if statement fails, as intended.

BTW, thanks for your feedback, it's important that people review stuff like this. All too often on XDA we just come up with something "that works" and everyone starts using it without thinking about what it actually does.
 

eric00716

Senior Member
Jun 29, 2012
173
60
Winder, GA
I think finding the exact location of the if statement or string would be tough, given that this has to work on multiple kernel versions. We have to stick to addresses that can be found with kallsysms_lookup_name.

I was already thinking about doing your second suggestion. I've searched through the code and I don't think there are any circumstances that would call get_partition_num_by_name("system"), but future kernel changes and other devices may be different. For now, this seems to be the only situation that will return 666 and the only consequence will be that the if statement fails, as intended.

BTW, thanks for your feedback, it's important that people review stuff like this. All too often on XDA we just come up with something "that works" and everyone starts using it without thinking about what it actually does.


Flar is there anyway to disable dt2w and s2w already built in to the kernel because my m8 wakes itself in my shirt pocket about 10 times a day. Thanks in advance
 

flar2

Recognized Developer
Jun 11, 2012
18,897
87,868
Southwestern Ontario
elementalx.org
I've updated the module a bit to make it easier to port to future kernels and other devices that use this form of write protection. All that needs to be done is to edit the CRC value for module_layout. I've also made it so the module can't be unloaded, we don't want to do that. In the process, I was able to reduce the module's overhead. Also, as per @m03sizlak's suggestion, I made it so it will only return the non-existent partition if the calling function is generic_make_request_checks.

The first version works, but we should start testing this version.


Download:
wp_mod.ko

(downloads not showing up for some reason, hold on)


Changes:
-only return non-existing partition number if called by generic_make_request_checks
-remove exit from module (we don't want to be able to unload it)
-clean up code
 
Last edited:

Wonders_Never_Cease

Inactive Recognized Contributor
Jul 10, 2013
2,739
1,558
attach it here then...just a thought...since not showing on goo.im

I've updated the module a bit to make it easier to port to future kernels and other devices that use this form of write protection. All that needs to be done is to edit the CRC value for module_layout. I've also made it so the module can't be unloaded, we don't want to do that. In the process, I was able to reduce the module's overhead. Also, as per @m03sizlak's suggestion, I made it so it will only return the non-existent partition if the calling function is generic_make_request_checks.

The first version works, but we should start testing this version.


Download:
wp_mod.ko

(downloads not showing up for some reason, hold on)


Changes:
-only return non-existing partition number if called by generic_make_request_checks
-remove exit from module (we don't want to be able to unload it)
-clean up code
 

Top Liked Posts

  • There are no posts matching your filters.
  • 74
    wp_mod: Module to disable system write protection

    This is a kernel module that disables write protection on the system partition while running the stock kernel.


    HTC changed the MMC_MUST_PREVENT_WP_VIOLATION code to make it much harder to crack. I had to redo the module completely, so this is experimental. In the past, it was a simple matter of changing a variable, now we have to replace a function in the kernel so it returns something different, causing the kernel to skip over the write protection code.

    I would caution against loading the module after attempting to make changes to the system partition. It could end up corrupting the filesystem. If the module is loaded at boot, there should be no worries.

    This module will probably need to be updated to load with future kernels when they are released.


    Please consider a donation to support ongoing development
    Many thanks to those who have donated!


    Download:

    wp_mod for GPE Marshmallow 6.0 can be found here:
    http://xdaforums.com/htc-one-m8/general/root-root-marshmallow-gpe-supersu-t3242210


    Sense 4.4.4 (thanks @migascalp):
    http://www.mediafire.com/download/4vyqslnc4crsnto/wp_mod_3.28.401.6.zip


    Sense 4.4.3 (2.22 base):
    wp_mod.ko

    Sense 4.4.2:
    wp_mod.ko

    GPE 4.4.4 (thanks to @italyforever):
    wp_mod.ko

    GPE 4.4.2:
    wp_mod.ko




    Installation:
    Wait for it to be implemented in your favourite ROM

    * or *

    Copy the module to your device, and type
    Code:
    su
    insmod /location-where-you-copied-it/wp_mod.ko


    Changes:

    April 2, 2014 - wp_mod 4.1
    -only return non-existing partition number if called by generic_make_request_checks
    -remove exit from module (we don't want to be able to unload it)
    -clean up code


    March 31, 2014 - wp_mod 4.0
    -new method for HTC One m8



    Source:
    https://github.com/flar2/wp_mod

    Module was compiled against m8 Google Play Edition source. Some symbol CRC checks had to be hexedited in the compiled module to match the stock kernel. Thanks to Michael Coppola for example of function hooking on arm: http://poppopret.org/2013/01/07/suterusu-rootkit-inline-kernel-function-hooking-on-x86-and-arm/#arm
    9
    AWESOME work flar2.

    After examining the source, it is indeed *much* more complicated than it has been in the past. Just curious, if you have the kernel source, what is to stop you from just rewriting the hooked functions instead of hijacking them with this code, which appears to be proof of concept code for ARM rootkits?

    Second question, the very informative page you linked to, and based this on, says this about ARM instruction caching:

    http://poppopret.org/2013/01/07/suterusu-rootkit-inline-kernel-function-hooking-on-x86-and-arm/#arm


    Any reason why you do not use this approach in your module?


    Last and possibly most important question. The page also says this:

    The code uses this approach only to avoid detection by rootkit detectors, something that we should have zero concerns about. Why not use the other approach, system call hooking by swapping out function pointers in the system call table?


    THANK YOU.

    I did rewrite the function. Remember, we have to do this in the running kernel. Whenever the original function is called, it jumps to my new function instead. Hooking/hijacking are the same thing. That site also shows how to hide the module and a bunch of other stealth stuff, but none of that was necessary for this.

    It's extremely easy to disable write protection if you compile your own kernel, you just turn off MMC_MUST_PREVENT_WP_VIOLATION.

    Previously, the wp_mod hack was dead simple. All we had to do was call an existing kernel function to change the number of the partition that write protection applied to. In the new source (below), HTC got rid of all this extraneous code and just hardcoded it to apply the write protection to /system. This happens in block/blk-core.c as you can see below. We need to skip over the quoted code.

    Code:
    static noinline_for_stack bool
    generic_make_request_checks(struct bio *bio)
    {
    
    ......
    
    #ifdef CONFIG_MMC_MUST_PREVENT_WP_VIOLATION
    	sprintf(wp_ptn, "mmcblk0p%d", get_partition_num_by_name("system"));   //hardcoded to look for system partition
    	if (!strcmp(bdevname(bio->bi_bdev, b), wp_ptn) && !board_mfg_mode() &&   //wp_ptn == mmcblk0p45  (/system)
    			(get_tamper_sf() == 1) && (bio->bi_rw & WRITE)) {
    		pr_info("blk-core: Attempt to write protected partition %s block %Lu \n",
    				bdevname(bio->bi_bdev, b), (unsigned long long)bio->bi_sector);
    		err = 0;
    		goto wp_end_io;
    	} else if (atomic_read(&emmc_reboot) && (bio->bi_rw & WRITE)) {
    		pr_info("%s: Attempt to write eMMC, %s block %Lu \n", current->comm,
    				bdevname(bio->bi_bdev, b), (unsigned long long)bio->bi_sector);
    		err = -EROFS;
    		goto wp_end_io;
    	}
    #endif
    
    ..............
    
    }


    It's a *bad idea* to replace a big complicated important function like static noinline_for_stack bool
    generic_make_request_checks() so I decided to modify a simpler function within it, get_partition_num_by_name(). I changed get_partition_num_by_name() to return a different partition number when name == system. I didn't see any code in the kernel source where it would cause a problem to return the wrong partition number for system. After loading wp_mod.ko, write protection is applied to a non-existent partition instead of /system. The end result is exactly the same as my old wp_mod that has proven to work on many devices.


    Why didn't I just change the address in the system call table? I don't think that is so easy on contemporary kernels. I found the function hooking method simpler and more foolproof.


    EDIT: in my haste while answering this at work, I quoted the wrong function containing the write protection code. It's static noinline_for_stack bool
    generic_make_request_checks not bio_check_eod (which is the function right above it in blk-core.c)
    9
    I've updated the module a bit to make it easier to port to future kernels and other devices that use this form of write protection. All that needs to be done is to edit the CRC value for module_layout. I've also made it so the module can't be unloaded, we don't want to do that. In the process, I was able to reduce the module's overhead. Also, as per @m03sizlak's suggestion, I made it so it will only return the non-existent partition if the calling function is generic_make_request_checks.

    The first version works, but we should start testing this version.


    Download:
    wp_mod.ko

    (downloads not showing up for some reason, hold on)


    Changes:
    -only return non-existing partition number if called by generic_make_request_checks
    -remove exit from module (we don't want to be able to unload it)
    -clean up code
    9
    wp_mod for Sense 6 Android 4.4.3 2.22.401.4

    wp_mod.ko
    8
    For users who have init.d support in their ROM. Flash this and your good to go

    https://mega.co.nz/#!XINyDIrB!QcdP3sZJjgKAivkEa7iN8Jusx0e78T1rpA5PT7VGAxQ

    Sent from my Note 3