Автор Тема: Достъп до sys_call_table в 2.6.32  (Прочетена 1087 пъти)

st0ne

  • Участници
  • ***
  • Публикации: 5
    • Профил
Достъп до sys_call_table в 2.6.32
« -: Mar 17, 2010, 02:11 »
От няколко дни се се опитвам да напиша LKM, с който дапроменя няколко функции от sys_call_table с чисто екпериментална и учебна цел, но нямам особен успех :( Липсата на export на sys_call_table не беше проблем - вземам адреса като използвам техниката с IDT. След като имам адреса понеже тази част от паметта е read-only извиквам set_memory_rw(sys_call_table, 1), но тя ми връща 0 и паметта наистина не е достъпна за писане. Видях, че преди е ставало с change_page_attr(), но тази ф-ция вече е deprecated. Все трябва да има някакъв начин. Нали все пак съм в ring 0 и имам уш пълен достъп до всичко. Ако някой може да ми обясни как точно действа това ограничение за read-only и как да го избегна ще съм му много благодарен. Мисля да пробвам с код написан на assembler да сменя стойностите, но не съм сигурен дали ще се получи.
Активен

gat3way

  • Напреднали
  • *****
  • Публикации: 6050
  • Relentless troll
    • Профил
    • WWW
Re: Достъп до sys_call_table в 2.6.32
« Отговор #1 -: Mar 17, 2010, 10:13 »
Няма да се получи, set_memory_rw проверява дали адреса не е в rodata секцията. Единият вариант е да видиш как се променят page атрибутите в arch/*/mm/pageattr.c и да се опиташ да си напишеш собствена функция, която заобикаля ограничението, обаче това не е особено лесна задача. Другият вариант е ако наистина ти е за експериментални цели, а не да си пишеш rootkit-ове, просто да си прекомпилираш ядрото. Отваряш arch/*/kernel/entry.S и просто променяш позволенията на страниците, заети от rodata секцията (там трябва да е описано нещо от сорта на .section .rodata,"a" - променяш го на .section .rodata, "aw" и прекомпилираш ядрото).

* се заменя с твоята архитектура (x86, x86_64, etc)


Активен

"Knowledge is power" - France is Bacon

st0ne

  • Участници
  • ***
  • Публикации: 5
    • Профил
Re: Достъп до sys_call_table в 2.6.32
« Отговор #2 -: Mar 17, 2010, 14:51 »
За експериментални цели ми е и точно за това искам да го направя без да прекомпилирам ядрото. За rootkit бих се насочил към нещо с debug регистрите, понеже доколкото съм чел то е най-трудно за откриване (като изключим концепцията за използване на хардуерна виртуализация, но поне на мен тя ми се вижда доста трудоемко начинание).

Не се бях сетил да проверя как е направено. Мога да се опитам да направя нещо подобно като гледам кода в pageattr.c, но щом казваш че е трудно не знам какъв успех ще имам. Тези дни ще пробвам и ще пиша.

А как точно работи тази проверка дали адреса е ro? Ако директно напиша някакъв асемблерски код, който сменя стойността на клетката в паметта как ще ме хване, че пипам по read-only? Извинявам се ако въпросите са малко глупави, но не открих никъде да е описано как стават тези неща.
Активен

gat3way

  • Напреднали
  • *****
  • Публикации: 6050
  • Relentless troll
    • Профил
    • WWW
Re: Достъп до sys_call_table в 2.6.32
« Отговор #3 -: Mar 17, 2010, 15:26 »
Ами ако погледнеш pageattr.c (визирам x86-кия), ще видиш че set_memory_rw() вика change_page_attr_set, която от своя страна вика __change_page_attr(). Някъде в тази функция има следното:

Код
GeSHi (C):
  1. new_prot = static_protections(new_prot, address, pfn);
  2.  


Във въпросната функция static_protections() се прави самата проверка:

Код
GeSHi (C):
  1.       /*
  2.          * The .rodata section needs to be read-only. Using the pfn
  3.          * catches all aliases.
  4.          */
  5.  
  6.        if (within(pfn, __pa((unsigned long)__start_rodata) >> PAGE_SHIFT,
  7.                   __pa((unsigned long)__end_rodata) >> PAGE_SHIFT))
  8.                pgprot_val(forbidden) |= _PAGE_RW;
  9.  
  10. ....
  11.  
  12. prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden));
  13.  
  14.  
  15.  

И дефакто, атрибутите на страницата не се променят щом като се намира във въпросната секция.
Активен

"Knowledge is power" - France is Bacon

st0ne

  • Участници
  • ***
  • Публикации: 5
    • Профил
Re: Достъп до sys_call_table в 2.6.32
« Отговор #4 -: Mar 21, 2010, 15:33 »
Реших, че може би ще е по-лесно направо с assembler да се обходи таблицата със страниците и да се сменят правата на подходящата страница. Ето един примерен код, който открих:

Код:
void *origaddr = (void*) 0;
void *origcr3 = (void*) 0;
void *direntry = (void*) 0;
void *mdentry = (void*) 0;

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

original_call = sys_call_table[__NR_sync];
origaddr = &sys_call_table[__NR_sync];

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

void myfunc(void)
{
__asm__ __volatile__
(
"pushl %eax\n\t"
        "pushl %ebx\n\t"
"movl %cr3, %eax\n\t"
"movl %eax, origcr3\n\t"
"andl $0xfffff000, %eax\n\t"
"addl $0xc0000000, %eax\n\t"
"movl origaddr, %ebx\n\t"
"shrl $22, %ebx\n\t"
"sall $2, %ebx\n\t"
"addl %ebx, %eax\n\t"
"movl (%eax), %eax\n\t"
"movl %eax, direntry\n\t"
"andl $0xfffff000, %eax\n\t"
"addl $0xc0000000, %eax\n\t"
"movl origaddr, %ebx\n\t"
"andl $0x003ff000, %ebx\n\t"
"shrl $12, %ebx\n\t"
"sall $2, %ebx\n\t"
"addl %ebx, %eax\n\t"
"movl %eax, %ebx\n\t"
"movl (%eax), %eax\n\t"
"andl $0xfffff000, %eax\n\t"
"addl $0x67, %eax\n\t"
"movl %eax, (%ebx)\n\t"
"movl %eax, mdentry\n\t"
"popl %ebx\n\t"
"popl %eax\n\t"
);
printk("origaddr->0x%p\n", origaddr);
printk("origcr3-->0x%p\n", origcr3);
printk("direntry->0x%p\n", direntry);
printk("mdentry-->0x%p\n", mdentry);
}

Тук myfunc() сменя правата на подходящата страница и след това може да се редактират стойностите. Кода е подходящ за 32 bit Intel/AMD с по-малко от 4G RAM. Сега съм си свалил manual-а за system development на Intel и ще се опитам да разбера какво точно става в асемблерския код. Не успях да намеря никъде обяснено как се обхожда таблицата със страниците и как се сменят права на страница.
Активен

gat3way

  • Напреднали
  • *****
  • Публикации: 6050
  • Relentless troll
    • Профил
    • WWW
Re: Достъп до sys_call_table в 2.6.32
« Отговор #5 -: Mar 21, 2010, 16:37 »
CR3 регистъра държи адреса към page директорията на процеса. Всеки процес си има отделен набор от page таблици и съответно при context switch, стойността на CR3 регистъра се променя.

Page директорията е нещо като масив от 32-битови стойности, съдържащ адреси на отделни pagetables. Въпросните pagetables са нещо като масив от 32-битови елементи, съдържащ адресите на отделните страници памет + атрибутите им. Дефакто става нещо като йерархична система от няколко нива - първото е page директорията, после page таблицата и накрая въпросната страница.

Ама това е за 32-битова система без PAE, с PAE нещата са организирани малко по-различно, както и при 64-битовите системи, има още едно ниво на йерархия и масивите са от 64-битови елементи.

Та не ми се рови в асемблерския код, но предполагам вземат стойността на CR3 регистъра, обхождат page директорията и нейните таблици и сет-ват въпросните битове (атрибути) когато открият търсената страница. Определено правят някакви магии с тези побитови маски, обаче нямам идея какви точно, трябва да хвана да прочета разни интелски документации за да схвана за какво иде реч.
Активен

"Knowledge is power" - France is Bacon

st0ne

  • Участници
  • ***
  • Публикации: 5
    • Профил
Re: Достъп до sys_call_table в 2.6.32
« Отговор #6 -: Mar 21, 2010, 17:43 »
Оказа се, че съвсем ще съм затруднен в моите начинания - процесора ми е с Intel EM64T. Тези процесори само IA-32e paging ли използват? Не съм сигурен понеже kernel release-а ми завършва на i686.PAE, а от документацията на Intel останах с впечатление, че Intel 64 използват само IA-32e paging.
Активен

gat3way

  • Напреднали
  • *****
  • Публикации: 6050
  • Relentless troll
    • Профил
    • WWW
Re: Достъп до sys_call_table в 2.6.32
« Отговор #7 -: Mar 21, 2010, 17:47 »
втф...това е интелската x86_64 архитектура. Ще ползва такъв paging механизъм, какъвто реши операционната система. Щом като е i686.PAE, нещата изглеждат така:

Активен

"Knowledge is power" - France is Bacon

st0ne

  • Участници
  • ***
  • Публикации: 5
    • Профил
Re: Достъп до sys_call_table в 2.6.32
« Отговор #8 -: Apr 10, 2010, 20:20 »
Имам известен напредък с PAE paging-а, но от вчера ударих на камък. Това е вариант за 4KB paging. Ето какво съм направил:

Код:
__asm__ __volatile__ ( "movl %%cr3, %%eax\n\t"

"addl $0xc0000000, %%eax\n\t"
 
"movl %2, %%ebx\n\t"
"shrl $30, %%ebx\n\t"

"movl (%%eax,%%ebx,0x8),%%ecx\n\t"
"movl 0x4(%%eax,%%ebx,0x8),%%edx\n\t"

"shrl $12, %%ecx  \n\t"

"movl %%edx, %%eax  \n\t"
"andl $0xfff, %%eax  \n\t"
"shll $20, %%eax  \n\t"

"addl %%eax, %%ecx  \n\t"

"movl %%ecx, %0  \n\t"

"shrl $12, %%edx  \n\t"
"andl $0xff, %%edx  \n\t"
"movl %%edx, %1  \n\t"

:"=r"(bits_low), "=r"(bits_high)
:"r" (addr)
:"%eax", "%ebx", "%ecx", "%edx"
                       );

Тук addr е адреса, чиято PTE трябва да се намери. Понеже от PDPTEi се вземат 40 бита (битове 51:12) аз ги съхранявам в две променливи - bits_low и bits_high. Първата съдържа ниските 32 бита, а втората високите 8. До тук изглежда, че всичко e наред. bits_high е нула при мен, но аз имам само 2GB RAM и това ми изглежда нормално. За ниските битове получавам стойност A8C.

Тази стойност използвам след това, за да прочета PDE-то на адреса.

Код:
__asm__ __volatile__ ( "movl %2, %%eax\n\t"
"addl $0xc0000000, %%eax\n\t"
 
"movl %1, %%ebx\n\t"
"shrl $21, %%ebx\n\t"
"andl $0x1ff, %%ebx\n\t"

"movl (%%eax,%%ebx, 0x8),%%ecx\n\t"
"movl %%ecx, %0\n\t"

:"=r"(pde_low_bits)
:"r" (addr), "r"(bits_low)
:"%eax", "%ebx", "%ecx"
                       );

bits_low сочи към началото на 4KB структура от 512 64-биови записа. Битове 29:21 от адреса оказват точния PDE запис. Би трябвало в pde_low_bits да имам първите 32 бита от PDE, но стойността е нула. Ако добавя отместване от 0x4, за да взема вторите 32 бита резултата пак е нула.

Не виждам грешка нито вгода, нито в логиката.
Активен