{"id":317,"date":"2013-07-01T11:18:54","date_gmt":"2013-07-01T11:18:54","guid":{"rendered":"http:\/\/www.selinuxplus.com\/?p=317"},"modified":"2013-07-01T11:28:20","modified_gmt":"2013-07-01T11:28:20","slug":"317","status":"publish","type":"post","link":"http:\/\/www.selinuxplus.com\/?p=317","title":{"rendered":"\u5173\u4e8e\u5185\u6838\u7cfb\u7edf\u8c03\u7528\u7684hooks\u95ee\u9898"},"content":{"rendered":"<p>\u5173\u4e8e\u5185\u6838\u7cfb\u7edf\u8c03\u7528\u7684hooks\u95ee\u9898<br \/>\n\u8f6c\u8f7d\u8bf7\u6ce8\u660e\u51fa\u5904 [ By selinux.com]<a href=\"http:\/\/www.selinuxplus.com\" title=\"selinux+\" target=\"_blank\">SELinux+<\/a><br \/>\n\u4ee3\u7801\u5730\u5740\uff1ahttps:\/\/github.com\/qfong\/mkm<br \/>\n\u622a\u83b7\u7cfb\u7edf\u8c03\u7528\u7684\u5e38\u7528\u7684\u65b9\u6cd5\u901a\u8fc7sys_call_table\u7684\u65b9\u6cd5\uff0c\u5982\uff1a<br \/>\n\u6ce8\u518chooks\u51fd\u6570\uff1a<\/p>\n<pre class=\"lang:default decode:true \" >ref_sys_read = (void *) sys_call_table[SYS_read];\r\nsys_call_table[__NR_read] = (unsigned long *)hooks_sys_read;<\/pre>\n<p>unhooks\u51fd\u6570<\/p>\n<pre class=\"lang:default decode:true \" >sys\uff3fcall_table[__NR_read] = (unsigned long *)ref_sys_read;<\/pre>\n<p>\u6ce8\u518chooks\u51fd\u6570\uff0c\u4f46\u7cfb\u7edf\u5fc5\u987b\u5bfc\u51fasys_call_table\u5185\u6838\u7b26\u5408\uff0c\u4f46\u57282.6\u5185\u6838\u548c\u90e8\u52062.4\u7684\u5185\u6838\u7cfb\u7edf\u4e2d\uff0csys_call_table\u4e0d\u518d\u5bfc\u51fa\uff0c\u4f46\u53ef\u4ee5\u5728\u5185\u5b58\u4e2d\u627e\u5230\u5b83\u3002<\/p>\n<p>\u4e0b\u9762\u7ed9\u51fa\u7684\u65b9\u6cd5\u662f\u57fa\u4e8e\u901a\u8fc7\u641c\u7d22\u4ee3\u7801\u5757\u6307\u9488\u4e3aSYS_CLOSE\uff0c\u5bfb\u627e\u4e00\u4e2a\u7279\u5b9a\u7684\u5185\u5b58\u6a21\u5f0f\uff0c\u7528\u4e8e\u53d1\u73b0sys_call_table\u7684\u6307\u9488\u5730\u5740\uff0c\u7136\u540e\u4f9d\u9760\u7684\u5730\u5740\u8868\u7684\u524d6\u4e2a\u5b57\u5230\u8be5\u5730\u5740\u3002\u8fd9\u662f\u5728x86\u7cfb\u7edf\u4ee5\u53ca\u4e00\u62db\uff0c\u4f46\u4e00\u5411\u88ab\u8ba4\u4e3a\u662f\u4e0d\u7a33\u5b9a\u548c\u4e0d\u592a\u53ef\u9760\u3002\u5982\u4e0b\u9762\u7684\u4ee3\u7801\uff1a<\/p>\n<pre class=\"lang:default decode:true \" >static unsigned long **acquire_sys_call_table(void)\r\n{\r\n    unsigned long int offset = PAGE_OFFSET;\r\n    unsigned long **sct;\r\n\r\n    while(offset &lt; ULLONG_MAX)\r\n    {\r\n        sct = (unsigned long **)offset;\r\n        if(sct[__NR_close] == (unsigned long *) sys_close)\r\n        {\r\n            return sct;\r\n        }\r\n        offset += sizeof(void *);\r\n    }\r\n    printk(KERN_INFO \"getting syscall table failed.\\n\");\r\n    return NULL;\r\n}<\/pre>\n<p>\u4f46\u4e5f\u6709\u4e00\u4e9b\u4f9d\u9760sys_call\u7684\u4e2d\u65ad\u5904\u7406\u7a0b\u5e8f\u5730\u5740\u4e2d\u65ad\u63cf\u8ff0\u7b26\u8868\uff0c\u4e2d\u65ad\u9677\u5165\u70b9\u4e3a0x80\u3002\u7136\u540e\u4e00\u4e2a\u641c\u7d22sys_call_table\u6307\u9488\u5730\u5740\u7684\u5904\u7406\u7a0b\u5e8f\uff0c\u7528\u4e8e\u67e5\u627e\u9700\u8981\u8df3\u8f6c\u5230\u7684\u786e\u5207sys_call\u4e2d\u65ad\u5904\u7406\u3002<\/p>\n<p>ARM Linux\u4e0a\u7684\u7cfb\u7edf\uff0c\u6ca1\u6709IDT(Interrupt Descriptor Table)\uff0c\u800c\u662f\u8f6f\u4ef6\u4e2d\u65ad\uff08SWI\uff09\u5904\u7406\u7a0b\u5e8f\u6240\u5728LDR\u6307\u5411\u7684\u5730\u57400x00000008\uff0c\u6216\u57280xFFFF0008\u6216\u9ad8\u5411\u91cf\u5b9e\u73b0(high-vector implementations)\u5982Android\u3002\u6309\u7167\u5730\u5740\uff0c\u67e5\u627e\u5e76\u52a0\u8f7d\u5176\u7cfb\u7edf\u8c03\u7528\u8868\u3002<\/p>\n<pre class=\"lang:default decode:true \" >unsigned long* find_sys_call_table()\r\n{\r\n  \/\/Address of the sofware interrupt (swi) handler in high vector ARM systems like Android\r\n  const void *swi_addr = 0xFFFF0008;\r\n \r\n  unsigned long* sys_call_table = NULL;\r\n  unsigned long* ptr = NULL;\r\n  unsigned long vector_swi_offset = 0;\r\n  unsigned long vector_swi_instruction = 0;\r\n  unsigned long *vector_swi_addr_ptr = NULL;\r\n \r\n  \/\/ Get the load pc instruction from the swi\r\n  memcpy(&amp;vector_swi_instruction, swi_addr, sizeof(vector_swi_instruction));\r\n  printk(KERN_INFO \"-&gt; vector_swi instruction %lx\\n\", vector_swi_instruction);\r\n \r\n  \/\/ Read the offset from the swi adress where the handler pointer lives\r\n  vector_swi_offset = vector_swi_instruction &amp; (unsigned long)0x00000fff;\r\n  printk (KERN_INFO \"-&gt; vector_swi offset %lx\\n\", vector_swi_offset);\r\n \r\n  \/\/ Get the pointer to the swi handler (offset is from the load instruction location + 2 words, due to ARM quirks) \r\n  vector_swi_addr_ptr = (unsigned long *)((unsigned long)swi_addr + vector_swi_offset + 8);\r\n  printk (KERN_INFO \"-&gt; vector_swi_addr_ptr %p, value %lx\\n\", vector_swi_addr_ptr, *vector_swi_addr_ptr);\r\n \r\n  \/************\r\n   * Starting at the beginning of the handler, search for the sys_call_table address load\r\n   * This code is the result of the \/arch\/arm\/kernel\/entry-common.S file, starting at the line\r\n   * ENTRY(vector_swi).  You'll see that there is always a zero_fp after saving register state\r\n   * before any function begins.  It's a good \"lighthouse\" to search for to make sure \r\n   * you've entered the stack-frame-proper before looking for the sys_call_table pointer load \r\n   * instruction\r\n   ************\/\r\n \r\n  ptr=*vector_swi_addr_ptr;\r\n  bool foundFirstLighthouse = false;\r\n  unsigned long sys_call_table_offset = 0;\r\n \r\n  printk (KERN_INFO \"-&gt; ptr %p, init_mm.end_code %lx\\n\", ptr, init_mm.end_code);\r\n \r\n  \/\/ Don't search past the end of the code block.  This is a dumb bound, I should be searching till I hit the\r\n  \/\/ equivalent of a ret in ARM, but I didn't feel like figuring it out since ARM doesn't have a ret instruction\r\n  while ((unsigned long)ptr &lt; init_mm.end_code &amp;&amp; sys_call_table == NULL)\r\n  {\r\n    \/\/ Find the zero_fp invocation (which translates into a load of zero into R11)\r\n    if ((*ptr &amp; (unsigned long)0xffff0fff) == 0xe3a00000)\r\n    {\r\n      foundFirstLighthouse = true;\r\n      printk (KERN_INFO \"-&gt; found first lighthouse at %p, value %lx\\n\", ptr, *ptr);\r\n    }\r\n \r\n    \/\/ Search for the loading of the sys_call_table (in entry-common.S, given as \"adr  tbl, sys_call_table\",\r\n    \/\/ which translates to an add and a ldr.  The add loads the sys_call_table pointer) \r\n    if (foundFirstLighthouse &amp;&amp; ((*ptr &amp; (unsigned long)0xffff0000) == 0xe28f0000))\r\n    {\r\n      \/\/ Get the offset from the add that will contain the actual pointer\r\n      sys_call_table_offset = *ptr &amp; (unsigned long)0x00000fff;\r\n      printk (KERN_INFO \"-&gt; sys_call_table reference found at  %p, value %lx, offset %lx\\n\", ptr, *ptr, \r\n \r\nsys_call_table_offset);\r\n \r\n      \/\/ Grab that damn pointer and get on with it!\r\n      sys_call_table = (unsigned long)ptr + 8 + sys_call_table_offset;\r\n      printk (KERN_INFO \"-&gt; sys_call_table found at %p\\n\", sys_call_table);\r\n      break;\r\n    }\r\n \r\n    ptr++;\r\n  }\r\n \r\n  return sys_call_table;\r\n}\r\n<\/pre>\n<p>\u4e0b\u9762\u662f\u4e00\u4e9b\u5176\u4ed6\u83b7\u5f97\u8c03\u7528\u8868\u7684\u65b9\u6cd5\uff1a<br \/>\nX86\u5e73\u53f0\u4e0a<\/p>\n<pre class=\"lang:default decode:true \" >Phrack #58 0x07; sd, devik\r\nunsigned long *find_sys_call_table ( void )\r\n{\r\n    char **p;\r\n    unsigned long sct_off = 0;\r\n    unsigned char code[255];\r\n\r\n    asm(\"sidt %0\":\"=m\" (idtr));\/\/\u5185\u5b58\u4e2d\u65ad\u6307\u4ee4\uff0c\u5f97\u5230\u4e2d\u65ad\u63cf\u8ff0\u7b26\u8868\u8d77\u59cb\u5730\u5740\r\n    memcpy(&amp;idt, (void *)(idtr.base + 8 * 0x80), sizeof(idt));\r\n    sct_off = (idt.off2 &lt;&lt; 16) | idt.off1;\r\n    memcpy(code, (void *)sct_off, sizeof(code));\r\n\r\n    p = (char **)memmem(code, sizeof(code), \"\\xff\\x14\\x85\", 3);\r\n\r\n    if ( p )\r\n        return *(unsigned long **)((char *)p + 3);\r\n    else\r\n        return NULL;\r\n}<\/pre>\n<p>X86_64\u5e73\u53f0\u4e0a<\/p>\n<pre class=\"lang:default decode:true \" >unsigned long *find_sys_call_table ( void )\r\n{\r\n    char **p;\r\n    unsigned long sct_off = 0;\r\n    unsigned char code[512];\r\n\r\n    rdmsrl(MSR_LSTAR, sct_off);\/\/\u83b7\u5f97system_call\u5730\u5740\r\n    memcpy(code, (void *)sct_off, sizeof(code));\r\n\r\n    p = (char **)memmem(code, sizeof(code), \"\\xff\\x14\\xc5\", 3);\r\n\r\n    if ( p )\r\n    {\r\n        unsigned long *sct = *(unsigned long **)((char *)p + 3);\r\n\r\n        \/\/ Stupid compiler doesn't want to do bitwise math on pointers\r\n        sct = (unsigned long *)(((unsigned long)sct &amp; 0xffffffff) | 0xffffffff00000000);\r\n        \/\/\u5728\u83b7\u5f97sys_call_table\u5730\u5740\u65f6\uff0c\u9700\u8981\u548c0xffffffff00000000\u76f8\u6216\uff0c\u5426\u5219\u53ef\u80fd\u5f15\u8d77\u5b95\u673a\r\n        return sct;\r\n    }\r\n    else\r\n        return NULL;\r\n}<\/pre>\n<p>ia32\u5e73\u53f0<\/p>\n<pre class=\"lang:default decode:true \" >\/\/ Obtain sys_call_table on amd64; pouik\r\nunsigned long *find_ia32_sys_call_table ( void )\r\n{\r\n    char **p;\r\n    unsigned long sct_off = 0;\r\n    unsigned char code[512];\r\n\r\n    asm(\"sidt %0\":\"=m\" (idtr));\/\/ \/\u6307\u5b9a\u5185\u5b58\u4e2d\u65ad\u63cf\u8ff0\u7b26\r\n    memcpy(&amp;idt, (void *)(idtr.base + 16 * 0x80), sizeof(idt));\r\n    sct_off = (idt.off2 &lt;&lt; 16) | idt.off1;\r\n    memcpy(code, (void *)sct_off, sizeof(code));\r\n\r\n    p = (char **)memmem(code, sizeof(code), \"\\xff\\x14\\xc5\", 3);\r\n\r\n    if ( p )\r\n    {\r\n        unsigned long *sct = *(unsigned long **)((char *)p + 3);\r\n\r\n        \/\/ Stupid compiler doesn't want to do bitwise math on pointers\r\n        sct = (unsigned long *)(((unsigned long)sct &amp; 0xffffffff) | 0xffffffff00000000);\r\n      \/\/\u9700\u8981\u548c0xffffffff00000000\u76f8\u6216\uff0c\u9632\u6b62\u5bc4\u5b58\u5668\u6ea2\u51fa\u5bfc\u81f4\u5b95\u673a\r\n        return sct;\r\n    }\r\n    else\r\n        return NULL;\r\n}<\/pre>\n<p>ARM<\/p>\n<pre class=\"lang:default decode:true \" >\/\/ Phrack #68 0x06; dong-hoon you\r\nunsigned long *find_sys_call_table ( void )\r\n{\r\n\tvoid *swi_addr = (long *)0xffff0008;\r\n\tunsigned long offset, *vector_swi_addr;\r\n\r\n\toffset = ((*(long *)swi_addr) &amp; 0xfff) + 8;\r\n\tvector_swi_addr = *(unsigned long **)(swi_addr + offset);\r\n\r\n\twhile ( vector_swi_addr++ )\r\n\t\tif( ((*(unsigned long *)vector_swi_addr) &amp; 0xfffff000) == 0xe28f8000 )\r\n        {\r\n\t\t\toffset = ((*(unsigned long *)vector_swi_addr) &amp; 0xfff) + 8;\r\n\t\t\treturn vector_swi_addr + offset;\r\n\t\t}\r\n\r\n\treturn NULL;\r\n}\r\n<\/pre>\n<p>\u5173\u4e8e\u5199\u4fdd\u62a4<br \/>\n\u7531\u4e8e\u5185\u6838\u7684\u9875\u6807\u8bb0\u4e3a\u53ea\u8bfb\uff0c\u5c1d\u8bd5\u7528\u51fd\u6570\u53bb\u5199\u8fd9\u4e2a\u533a\u57df\u7684\u5185\u5b58\uff0c\u4f1a\u4ea7\u751f\u4e00\u4e2a\u5185\u6838oops\u3002\u8fd9\u79cd\u4fdd\u62a4\u53ef\u4ee5\u5f88\u7b80\u5355\u7684\u88ab\u89c4\u907f\uff0c\u4f46\u901a\u8fc7\u8bbe\u7f6ecr0\u5bc4\u5b58\u5668\u7684WP\u4f4d\u4e3a0\uff0c\u7981\u6b62\u5199\u4fdd\u62a4CPU\u4e0a\u3002\u63a7\u5236\u5bc4\u5b58\u5668\u7ef4\u57fa\u767e\u79d1\u7684\u6587\u7ae0 \u8bc1\u5b9e\u4e86\u8fd9\u4e00\u70b9\u5c5e\u6027\uff1a<\/p>\n<p>\u4f4d\t\u540d\u79f0\t\u5168\u540d\t\u8bf4\u660e<br \/>\n16\tWP\t\u5199\u4fdd\u62a4\t\u786e\u5b9aCPU\u662f\u5426\u53ef\u4ee5\u5199\u5165\u9875\u9762\u6807\u8bb0\u4e3a\u53ea\u8bfb<br \/>\nWP\u4f4d\u5c06\u9700\u8981\u5728\u4ee3\u7801\u4e2d\u7684\u591a\u4e2a\u70b9\u7684\u8bbe\u7f6e\u548c\u91cd\u7f6e\uff0c\u6240\u4ee5\u5b83\u4f7f\u62bd\u8c61\u7684\u64cd\u4f5c\u7eb2\u9886\u6027\u610f\u4e49\u3002\u4e0b\u9762\u7684\u4ee3\u7801\u6765\u6e90\u4e8ePAX\u9879\u76ee\uff08http:\/\/pax.grsecurity.net\/\uff09\uff0c\u4e13\u95e8\u4ecenative_pax_open_kernel\uff08\uff09\u548cnative_pax_close_kernel\uff08\uff09\u4f8b\u7a0b\u3002\u91c7\u53d6\u683c\u5916\u8c28\u614e\uff0c\u4ee5\u9632\u6b62\u6f5c\u5728\u7684\u7ade\u4e89\u6761\u4ef6\u5f15\u8d77\u7531\u5012\u9709\u7684\u8c03\u5ea6\u5728SMP\u7cfb\u7edf\uff0c\u7531\u4e39\u2022\u7f57\u68ee\u4f2f\u683c\u5728\u4e00\u7bc7\u535a\u5ba2\u6587\u7ae0\u4e2d\uff08http:\/\/vulnfactory.org\/blog\/2011\/08\/12\/wp-safe-or-not\/\uff09\u89e3\u91ca\uff1a<br \/>\nAs described in the Intel Manuals (Volume 3A, Section 2.5):<\/p>\n<pre class=\"lang:default decode:true \" >WP        Write Protect (bit 16 of CR0) - When set, inhibits supervisor-level proce-\r\n          dures from writing into read-only pages; when clear, allows supervisor-level\r\n          procedures to write into read-only pages (regardless of the U\/S bit setting;\r\n          see Section 4.1.3 and Section 4.6). This flag facilitates implementation of the\r\n          copy-on-write method of creating a new process (forking) used by operating\r\n          systems such as UNIX.<\/pre>\n<p>Code\u5982\u4e0b\uff1a<\/p>\n<pre class=\"lang:default decode:true \" >inline unsigned long disable_wp ( void )\r\n{\r\n    unsigned long cr0;\r\n\r\n    preempt_disable();\/\/\u62a2\u5360\u5185\u6838\u7684\u63a7\u5236\r\n    barrier();\r\n\r\n    cr0 = read_cr0();\r\n    write_cr0(cr0 &amp; ~X86_CR0_WP);\r\n    return cr0;\r\n}\r\n\r\ninline void restore_wp ( unsigned long cr0 )\r\n{\r\n    write_cr0(cr0);\r\n\r\n    barrier();\r\n    preempt_enable_no_resched();\r\n}<\/pre>\n<p>\u4e5f\u53ef\u4ee5\u901a\u8fc7\u4ee5\u4e0b\u4ee3\u7801\u6765\u5b9e\u73b0<\/p>\n<pre class=\"lang:default decode:true \" >\/*\r\n*\u68c0\u6d4bCR0\u5199\u4fdd\u62a4\u4f4d\u662f\u5426\u8bbe\u7f6e\uff0c\u5982\u679c\u6ca1\u6709\uff0c\u9875\u5199\u4fdd\u62a4\u5df2\u88ab\u7981\u7528\uff0c\u5e76\u5c06bitwise-AND\u768416\u4f4d\u7f6e0\uff0c\u5e76\u8bbe\u7f6eCR0\r\n*\/\r\nstatic void disable_page_protection(void)\r\n{\r\n    unsigned long value;\r\n    asm volatile(\"mov %%cr0, %0\" : \"=r\"(value));\r\n    if(!(value &amp; 0x00010000))\r\n    {\r\n        return ;\r\n    }\r\n\r\n    asm volatile(\"mov %0, %%cr0\" : : \"r\"(value &amp; ~0x00010000));\r\n}\r\n\r\nstatic void enable_page_protection(void)\r\n{\r\n    unsigned long value;\r\n    asm volatile(\"mov %%cr0, %0\" : \"=r\"(value));\r\n\r\n    if((value &amp; 0x00010000))\r\n    {\r\n        return ;\r\n    }\r\n    asm volatile(\"mov %0, %%cr0\" : : \"r\"(value|0x00010000));\r\n}\r\n<\/pre>\n<p>\u5728\u4e00\u4e9bARM\u4e2d\uff0cWP\u4f4d\u7684\u6982\u5ff5\u4e0d\u5b58\u5728\uff0c\u5fc5\u987b\u91c7\u53d6\u7279\u6b8a\u7167\u987e\uff0c\u800c\u5728ARM\u4e2d\u5f15\u5165\u4e86\u6307\u4ee4\u7f13\u5b58\u67b6\u6784\u6765\u5904\u7406\u6570\u636e\u3002\u867d\u7136\u6570\u636e\u548c\u6307\u4ee4\u9ad8\u901f\u7f13\u5b58\u7684\u6982\u5ff5\u4e5f\u5b58\u5728x86\u548cx86_64\u786c\u4ef6\u67b6\u6784\u7684\uff0c\u8fd9\u6837\u7684\u529f\u80fd\u6ca1\u6709\u5728\u53d1\u5c55\u8fc7\u7a0b\u4e2d\u6784\u6210\u969c\u788d\u3002\u5728Android\u91cc\u9762\uff0c\u53ef\u4ee5\u4e0d\u9700\u8981\u5bf9\u5185\u5b58\u4fdd\u62a4\u673a\u5236\u8fdb\u884c\u8003\u8651\u3002<\/p>\n<p>\u5b9e\u73b0\u4ee3\u7801\u5730\u5740\uff1ahttps:\/\/github.com\/qfong\/mkm<br \/>\n\u9644\u6587\u6863\uff1a\u3000\u4e39\u2022\u7f57\u68ee\u4f2f\u683c\u5728\u4e00\u7bc7\u535a\u5ba2\u6587\u7ae0<br \/>\nWP: Safe or Not?<\/p>\n<p>During the course of kernel exploitation (or some other form of runtime kernel modification), it is frequently desirable to be able to modify the contents of read-only memory. On x86, a classic trick is to leverage the WP (write-protect) bit in the CR0 register.<\/p>\n<p>As described in the Intel Manuals (Volume 3A, Section 2.5):<\/p>\n<p>WP        Write Protect (bit 16 of CR0) &#8211; When set, inhibits supervisor-level proce-<br \/>\n          dures from writing into read-only pages; when clear, allows supervisor-level<br \/>\n          procedures to write into read-only pages (regardless of the U\/S bit setting;<br \/>\n          see Section 4.1.3 and Section 4.6). This flag facilitates implementation of the<br \/>\n          copy-on-write method of creating a new process (forking) used by operating<br \/>\n          systems such as UNIX.<br \/>\nIn an exploit where code execution has been achieved and the attacker wishes to, for example, install hooks in a read-only data structure, a simple solution is to toggle this bit to 0, perform the write, and toggle it back. This technique has been well-known for years, and is not only used in rootkits but also in commercial anti-virus products (is there a difference?).<\/p>\n<p>In practice, this approach works nearly all of the time. But there are some caveats to be aware of when using this trick in exploit development.<\/p>\n<p>Scheduling Race<\/p>\n<p>On SMP systems, there is a scheduling race that must be dealt with. In extremely unlucky circumstances, it\u2019s possible that the current thread disables the WP bit, is scheduled out at a precise moment, is re-scheduled onto a CPU that still has the WP bit enabled, and faults when attempting to perform a write to read-only memory. Even though I\u2019ve never seen this happen in practice, it\u2019s easy enough to contend with. If this is being done via some mechanism where you have the capability to compile against the current kernel (e.g. a module), one correct way of addressing this is to disable preemption of the current thread while performing writes to read-only pages, as the PaX project does:<\/p>\n<p>static inline unsigned long native_pax_open_kernel(void)<br \/>\n{<br \/>\n    unsigned long cr0;<\/p>\n<p>    preempt_disable();<br \/>\n    barrier();<br \/>\n    cr0 = read_cr0() ^ X86_CR0_WP;<br \/>\n    BUG_ON(unlikely(cr0 &#038; X86_CR0_WP));<br \/>\n    write_cr0(cr0);<br \/>\n    return cr0 ^ X86_CR0_WP;<br \/>\n}<\/p>\n<p>static inline unsigned long native_pax_close_kernel(void)<br \/>\n{<br \/>\n    unsigned long cr0;<\/p>\n<p>    cr0 = read_cr0() ^ X86_CR0_WP;<br \/>\n    BUG_ON(unlikely(!(cr0 &#038; X86_CR0_WP)));<br \/>\n    write_cr0(cr0);<br \/>\n    barrier();<br \/>\n    preempt_enable_no_resched();<br \/>\n    return cr0 ^ X86_CR0_WP;<br \/>\n}<br \/>\nThese two functions, pax_open_kernel() and pax_close_kernel(), are used to modify structures such as the IDT when the PAX_KERNEXEC feature is enabled.<\/p>\n<p>If you\u2019re performing these modifications in an exploit, a simpler solution is to leverage the cli (clear interrupt flag) and sti (set interrupt flag) instructions to disable interrupts entirely during the course of the writes, which prevents re-scheduling as a side effect:<\/p>\n<p>.macro disable_wp<br \/>\n    cli<br \/>\n    mov eax,cr0<br \/>\n    and eax,0xfffeffff<br \/>\n    mov cr0,eax<br \/>\n.endm<\/p>\n<p>.macro enable_wp<br \/>\n    mov eax,cr0<br \/>\n    or eax,0x10000<br \/>\n    mov cr0,eax<br \/>\n    sti<br \/>\n.endm<br \/>\nXen<\/p>\n<p>The scheduling issue can easily be worked around, but twiz mentioned to me that there may be problems doing this on Xen. When not using HAP (hardware-assisted paging), Xen handles paging by creating shadow page tables, mapping the guest\u2019s page tables read-only, and relying on WP to cause writes to guest page tables to trap and be handled properly by the hypervisor. As a result, CR0.WP was forcibly enabled in the past. This did not apply when using HAP, where the guest has always been able to freely access CR0.<\/p>\n<p>However, in 2007, Xen added support for emulating the behavior of CR0.WP in order to support software that relies on WP modification (e.g. anti-virus). When the guest faults on a non-user write to a resident page while CR0.WP=0, the faulting instruction is then emulated to allow the write to succeed. This feature is limited by the completeness of the x86 emulator, but everything except the most esoteric instructions should be emulated properly. As a result, leveraging the WP bit to write to read-only memory on Xen should not pose any problems for exploit developers.<\/p>\n<p>Thanks<\/p>\n<p>Thanks to twiz for suggesting looking at Xen, and Keir Fraser for helpful information.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u5173\u4e8e\u5185\u6838\u7cfb\u7edf\u8c03\u7528\u7684hooks\u95ee\u9898 \u8f6c\u8f7d\u8bf7\u6ce8\u660e\u51fa\u5904 &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[40,45],"tags":[39,50,37,49],"_links":{"self":[{"href":"http:\/\/www.selinuxplus.com\/index.php?rest_route=\/wp\/v2\/posts\/317"}],"collection":[{"href":"http:\/\/www.selinuxplus.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.selinuxplus.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.selinuxplus.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.selinuxplus.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=317"}],"version-history":[{"count":8,"href":"http:\/\/www.selinuxplus.com\/index.php?rest_route=\/wp\/v2\/posts\/317\/revisions"}],"predecessor-version":[{"id":325,"href":"http:\/\/www.selinuxplus.com\/index.php?rest_route=\/wp\/v2\/posts\/317\/revisions\/325"}],"wp:attachment":[{"href":"http:\/\/www.selinuxplus.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=317"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.selinuxplus.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=317"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.selinuxplus.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=317"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}