Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.08.22;
Скачать: CL | DM;

Вниз

Загрузка и выгрузка dll в чужой процесс   Найти похожие ветки 

 
bar   (2004-07-08 15:16) [0]

Загружаю свой дллку в процесс explorer`a с помощью хука на WH_GETMESSAGE. Загрузка проходит нормально. Но потом не могу выгрузить эту библиотеку. Хотя по идеи всё должно быть OK. Дескриптор загруженной в эксплорер библиотеки я первоночально сохранял в FileMapping и потом пытался выгрузить дллку по этому дескриптору из хука эксплорера(адресного пространства). Потом заметил, что при очередной загрузки loadlibrary возвращает одинаковый дескриптор уже загруженной дллки и использовал это для выяснения дескриптора библиотеки в адреснов пространстве explorer`а, но все равно не выгружается. Ж:-(
Помогите кто понял суть проблемы, может предложите другой мметод загрузки и выгрузки библиотеки в чужой процесс или объясните где у меня ошибка.

Пример кода из библиотеки хука.
type
   PGlobalDLLData = ^TGlobalDLLData;
   TGlobalDLLData = packed record
       SysHook: HWND; // дескриптор установленной ловушки
       MyAppWnd: HWND; // дескриптор нашего приложения
       Flag: Boolean;
       HLIB: THANDLE;
   end;
var
   GlobalData: PGlobalDLLData;
   MMFHandle: THandle;
   H: THAndle;
   GetAndSet: procedure(h: integer); stdcall; // процедура из 2-ой ДЛЛки, которая заменит оконную процедуру часов.

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

//Процедура хука
function CallWndProc(
   nCode: integer; // hook code
   wParam: WPARAM; // current-process flag
   lParam: LPARAM // address of structure with message data
   ): LRESULT; stdcall;
var
   hl: THAndle;
   ok, a: boolean;
begin
   if nCode = HC_ACTION then
   begin
       ok := false;
       if tmsg(pointer(lparam)^).hwnd = H then
       begin
           ok := true;
           if GlobalData^.Flag then
{ Если GlobalData^.Flag=true загружаем дллку в процесс окна с хендлом H}
           begin
               GlobalData^.HLIB := loadlibrary(remdll);
               @GetAndSet := nil;
               if GlobalData^.HLIB > HINSTANCE_ERROR then
               begin
                   @GetAndSet := GetProcAddress(GlobalData^.HLIB, "GetAndSet");
                   if @GetAndSet <> nil then
                       GetAndSet(H)
               end else
           end else
           begin{ Если GlobalData^.Flag=false загружаем дллку в процесс окна с хендлом H}
               PostMessage(H, WM_User + 456, 0, 0);
               sleep(1000);
               hl := loadlibrary(remdll);
               repeat
                   a := FreeLibrary(Hl);
                   PostMessage(GlobalData^.MyAppWnd,
                       WM_MYCMD, $55, HL);
                   PostMessage(GlobalData^.MyAppWnd,
                       WM_MYCMD, $56, integer(a));
               until a;
           end;
       end;
       result := CallNextHookex(GlobalData^.SysHook, ncode, wparam, lparam);
       if ok then

           UnHookWindowsHookEx(GlobalData^.SysHook);
   end
   else
       result := CallNextHookex(GlobalData^.SysHook, ncode, wparam, lparam);
   //
end;
......


 
DeadMeat ©   (2004-07-08 23:50) [1]

Дай угадаю... Ты прочитал статью на xakep.ru, про невидимый процесс?? Просто интересно...

---
...Death Is Only The Begining...


 
Kerk ©   (2004-07-09 08:31) [2]

Попробуй GetModuleHandle / FreeLibrary


 
Digitman ©   (2004-07-09 08:41) [3]


> bar   (08.07.04 15:16)  


открой процесс эксплорера (см. OpenProcess)

запроси в АП эксплорера блок вирт.памяти c параметром защиты страниц PAGE_EXECUTE_READWRITE под дальнейшее размещение там кода поточной ф-ции (см. VirtualAllocEx)

скопируй в этот блок памяти код поточной ф-ции (см. WriteProcessMemory)

стартуй в АП эксплорера удаленный кодовый поток вызовом CreateRemoteThread(), в качестве параметра, указывающего адрес поточной ф-ции, передай адрес точки входа в скопированную поточную ф-цию в АП эксплорера

при удаленном старте потока поточная ф-ция получает управление и загружает твою библиотеку в АП эксплорера, после чего завершается


 
Digitman ©   (2004-07-09 08:49) [4]


> bar   (08.07.04 15:16)  


а если уж пошел методом глоб.хука, то выкрутасы с обработкой WH_GETMESSAGE и иже с ним вовсе не нужны для этой цели

в момент загрузки хук-модуля в АП процесса эксплорера процедура иниц-ции хук-модуля получает управление, и в этот момент как раз и можно выполнить loadlibrary() ... но перед тем как выполнить loadlibrary() процедура иниц-ции хук-модуля должна проанализировать имя тек.процесса и выполнить loadlibrary() лишь в том случае, если вызов GetModuleFileName вернул именно explorer.exe, иначе библ-ка будет загружена во все GUI-процессы, а не только в эксплорер


 
SammIk ©   (2004-07-09 09:50) [5]

Дескриптор возвращаемыи лоадлиброри, это адрес
куда грузится библеотека.
А винлоадер старается грузить их в одно место,
по этому возвращает один хендл


 
bar   (2004-07-09 10:14) [6]

>>DeadMeat ©   (08.07.04 23:50) [1]
Нет не процесс невидимка. Просто нужно отловить некоторые сообщения SHELLDLL_DefView, а хук на него не работает(Runtime error 216 at FF640227 при нажатии win+d )( см ветку)
http://delphimaster.net/view/4-1089095680/
Кстати кто нибудь знает что такое Runtime error 216 ?

>>Digitman ©   (09.07.04 08:49) [4]
Знаю. Делал загрузку dll-ки в процесс в момент загрузки библиотеки хука, результат такой же, даже гдето глюков больше (скорее всего код корявый)а с WH_GETMESSAGE это второй более отлаженный вариант. Кроме того разве влияет то кога грузить dll.?
>>Digitman ©   (09.07.04 08:41) [3]
а как её потом выгрузить

>>Kerk ©   (09.07.04 08:31) [2]
Попробуй GetModuleHandle / FreeLibrary
В каком процессе? В споем приложении пробовал http://delphiworld.narod.ru/base/kill_dll.html не работает. В процессе explorer`a сейчас попробую.


 
DeadMeat ©   (2004-07-09 10:23) [7]


> Нет не процесс невидимка

Ну извини... Просто код взят из этой статьи...

---
...Death Is Only The Begining...


 
bar   (2004-07-09 10:38) [8]

>>DeadMeat ©   (09.07.04 10:23) [7]
Ну да, скоприровал откуда-то и подправил для свох нужд.
Но скопировал не с xakep.ru
Кстати а там про выгрузку длл есть инфа. Пойду посмотрю.


 
Тимохов ©   (2004-07-09 10:41) [9]

не совсем въехал про что тут говорится (квалификации не хватает), но про запуск потока в другом прочессе через загрузку/выгрузку библиоткb подробно рассказывается в главе 21, 22 или 23 рихтера 4 издание (ключевое слово для поиска CreateRemoteThread).


 
Digitman ©   (2004-07-09 10:42) [10]


> bar   (09.07.04 10:14) [6]


> как её потом выгрузить


все просто - вновь стартуй удаленный трэд, который выполнит freelibrary()


 
Тимохов ©   (2004-07-09 10:50) [11]

Это не офф, т.к. в струе вопроса.

> Digitman ©   (09.07.04 10:42) [10]

Я так понимаю, что Вы имеете в виду стартовать удаленный поток с поточкной функцией, равной FreeLibrary. Но при этом должна быть уверенность в том, что kernel32.dll загружена по тому же базовому адресу. По факту оказывается именно так - kernel32.dll загруже всегда по одному и тому же базовому адресу. Я уже подинмал этот вопрос, но тогда ответа не получил  - где факт того, что kernel32.dll загружена по одному адресу описан в msdn?


 
bar   (2004-07-09 11:12) [12]

>>Digitman ©   (09.07.04 10:42) [10]
А пример можно пожалуйста. А то я с WriteProcessMemory не общался.


 
Digitman ©   (2004-07-09 11:16) [13]


> Тимохов ©   (09.07.04 10:50) [11]
> Это не офф, т.к. в струе вопроса.
>
> > Digitman ©   (09.07.04 10:42) [10]
>
> Я так понимаю, что Вы имеете в виду стартовать удаленный
> поток с поточкной функцией, равной FreeLibrary


неправильно понимаешь
поточная ф-ция вызывает FreeLibrary

> bar   (09.07.04 11:12) [12]
> >>Digitman ©   (09.07.04 10:42) [10]
> А пример можно пожалуйста. А то я с WriteProcessMemory не
> общался.


а почитать для начала справку и первоисточники ?


 
bar   (2004-07-09 11:20) [14]

Я думал Вам просто дать пример. Или ссылку хорошую по теме.
Ну ладно буду искать.

см http://delphimaster.net/view/4-1089095680/
А первоисточники на русском не посоветуете. Лучше в инете.


 
Digitman ©   (2004-07-09 11:23) [15]


> bar   (09.07.04 11:20) [14]


ну держи, не жалко

на авторство не претендую, текст этот взят когда-то где-то только для анализа варианта решения


//
// внедрение dll в чужой процесс с помощью CreateRemoteThread.
// работать будет только там, где эта самая CreateRemoteThread реализована,
// а это nt/w2k/xp
// использовать так:
//   loader.exe идентификатор_процесса  полный_путь_и_имя_dll
// paul_shmakov@mail.ru
//
program loader;

{$APPTYPE CONSOLE}

uses
Windows, Messages, SysUtils;

function SetDebugPriv: Boolean;
var
Token: THandle;
tkp: TTokenPrivileges;
begin
Result := false;
if OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, Token) then
begin
  if LookupPrivilegeValue(nil, PChar("SeDebugPrivilege"), tkp.Privileges[0].Luid) then
  begin
    tkp.PrivilegeCount := 1;
    tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
    Result := AdjustTokenPrivileges(Token, false, tkp, 0, PTokenPrivileges(nil)^, PDWord(nil)^);
  end;
end;
end;

function Start(ProcessID: Cardinal; DllFileName: string): Boolean;
var
hProcess, hTh: THandle;
BytesWritten, ThreadID, DllNameLen: Cardinal;
LoadLibraryProc, MemPtr: Pointer;
ExitCode: DWord;
begin
Result := false;

//
// этот вызов нужен только для внедрения в системные процессы
// btw, нужны привилегии администратора
//
SetDebugPriv();

hProcess := OpenProcess(PROCESS_CREATE_THREAD or PROCESS_VM_OPERATION or PROCESS_VM_WRITE,
  true, ProcessID);

if hProcess <> 0 then
begin
  DllNameLen := Length(DllFileName) + 1;

  MemPtr := VirtualAllocEx(hProcess, nil, DllNameLen, MEM_COMMIT, PAGE_READWRITE);

  if MemPtr <> nil then
  begin
    if WriteProcessMemory(hProcess, MemPtr, PChar(DllFileName), DllNameLen, BytesWritten) then
    begin
      LoadLibraryProc := GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");

      hTh := CreateRemoteThread(hProcess, nil, 0, LoadLibraryProc, MemPtr, 0, ThreadID);

      if hTh <> 0 then
      begin
        if (WaitForSingleObject(hTh, INFINITE) = WAIT_OBJECT_0) and
          GetExitCodeThread(hTh, ExitCode) then
          Result := ExitCode <> 0;

        CloseHandle(hTh);
      end;
    end;

    VirtualFreeEx(hProcess, MemPtr, 0, MEM_RELEASE);
  end;

  CloseHandle(hProcess);
end;

end;

var
ProcessID: Cardinal;
DllName: string;
begin
if ParamCount < 2 then
begin
  WriteLn("usage: loader.exe process_id dll_full_path_and_name");
  Exit;
end;

ProcessID := StrToInt(ParamStr(1));
WriteLn("process id: " + IntToStr(ProcessID));

DllName := ParamStr(2);
WriteLn("dllname: " + DllName);

if ProcessID <> 0 then
begin
  if Start(ProcessID, DllName) then
    WriteLn("success")
  else
    WriteLn("fail");
end;
end.



 
Тимохов ©   (2004-07-09 11:27) [16]


>
> Digitman ©   (09.07.04 11:16) [13]
> неправильно понимаешь
> поточная ф-ция вызывает FreeLibrary

Да?
У рихтера пример имеено с передачей FreeLibrary в качестве поточной функции

    // Get the real address of LoadLibraryW in Kernel32.dll
     PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
        GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "FreeLibrary");
     if (pfnThreadRtn == NULL) __leave;

     // Create a remote thread that calls LoadLibraryW(DLLPathname)
     hThread = CreateRemoteThread(hProcess, NULL, 0,
        pfnThreadRtn, me.modBaseAddr, 0, NULL);
     if (hThread == NULL) __leave;


Все-таки - где доказательство того, что kernel32.dll всегда по одному адресу? Понимаю, что вопрос не совсем практичен, т.к. это и так ясно - но все же документальное подтверждение было бы не лишним.


 
Игорь Шевченко ©   (2004-07-09 11:30) [17]


> Все-таки - где доказательство того, что kernel32.dll всегда
> по одному адресу?


Практика - критерий истины


 
Тимохов ©   (2004-07-09 11:34) [18]


> Игорь Шевченко ©   (09.07.04 11:30) [17]
> Практика - критерий истины


Если бы меня спросли на интервью при приеме на работу "Всегда ли kernel32.dll расположена по одному базовому адресу". Я бы сказал конечно да и был бы прав. Так что здесь не вопрос опыта. А вопрос скрытых пока от меня мест в документации, которые не плохо бы иметь на примете :)))


 
Digitman ©   (2004-07-09 11:36) [19]


> Тимохов ©   (09.07.04 11:27) [16]


> Да?
> У рихтера пример имеено с передачей FreeLibrary в качестве
> поточной функции


вот ты заладил - Рихтер да Рихтер) ...да я понятия не имею кто такой Рихтер !) ... ну не читал я его ни разу, хоть и на слуху он без конца здесь, иными источниками пользовался и пользуюсь ..


> Все-таки - где доказательство того, что kernel32.dll всегда
> по одному адресу?


ну нашута это надо - трюки эти ? ничем они не оправданы, кроме сомнительной экономии пару-тройки сотен байт АП целевого процесса ..


 
Тимохов ©   (2004-07-09 11:41) [20]


> Digitman ©   (09.07.04 11:36) [19]

Я вас не понимаю.
Особенно это

> ну нашута это надо - трюки эти

Какие трюки?
Вы сами привели пример, где адрес LoadLibraryA находите в одном процессе, а запускаете поток в другом. Ясно, что вы предполагаете, что адрес загрузки kernel32.dll одинаков. Вот и вопрос - почему вы так полагаете. Игорь говорит, что "практика критерий истинины". Все же кроме практики должна быть дока! :)

А вот это вообще к чему?

> кроме сомнительной экономии пару-тройки сотен байт АП целевого
> процесса ..


ЗЫ. А рихтер, что рихтер. Виноват я что-ли, что книжка хорошая.


 
bar   (2004-07-09 11:43) [21]

>>Digitman ©   (09.07.04 11:23) [15]
Спасибо большое. Буду пробовать.
А кто такой Рихтер??


 
Игорь Шевченко ©   (2004-07-09 11:44) [22]

bar   (09.07.04 11:43)


> А кто такой Рихтер??


Пианист


 
Тимохов ©   (2004-07-09 11:46) [23]


> bar   (09.07.04 11:43) [21]

Это дядька такой, который написал хорошую книгу по windows.

http://www.books.ru/shop/books/8283


 
Digitman ©   (2004-07-09 12:10) [24]


> Тимохов ©   (09.07.04 11:41) [20]


это как раз пример того самого трюка, который при любом раскладе с "практикой" я бы не рекомендовал применять

и приведен он НЕ для подражания, а в качестве примера использования вызова WriteProcessMemory() (см. [12]) и сопутствующих вызовов .. не более того


 
Тимохов ©   (2004-07-09 12:14) [25]


> Digitman ©   (09.07.04 12:10) [24]

делаю выводы (подвожу итог):
1. Вы считаете, что закладываться на одинаковость базового адреса Kernel32.dll во всех процессах из области трюков, которые исползовать не нужно.
2. У вас есть свое ноухау как можно сделать CreateRemoteThread без того, чтобы полагаться на удиный базовый адрес Kernel32.dll, а например в удаленном процессе явно строите поточную фунцию, с использованием WriteProcessMemory.


 
Digitman ©   (2004-07-09 12:28) [26]


> Тимохов ©   (09.07.04 12:14) [25]


1. Все верно. На то что недокументировано полагаться нельзя при любом раскладе.

2. Никаких ноу-хау. Все это старо как мир - с пом. WriteProcessMemory() из АП тек.процесса в АП целевого процесса копируется готовый к "употреблению" машкод, который при получении управления вызовает документированную ф-цию LoadLibrary или FreeLibrary


 
Тимохов ©   (2004-07-09 12:34) [27]


> Digitman ©   (09.07.04 12:28) [26]

Как бальзам на душу :)))))
Я честно говоря так не делал - т.к. не особо силен в создании машкода да еще готового к употреблению, но если бы знал как писать машкод, то делал бы именно так.

Я еще когда рихтера читал - как-то мне это не понравилось: полагаться на однозначность базового адреса kernel32.dll.

Спасибо, теперь, если придется это делать, буду знать, что нужно копировать маш код...


 
Тимохов ©   (2004-07-09 12:37) [28]

Скажу честно, то, что написано в 26 я и хотел услышать и в процессе прошлого обсуждения и сейчас.
:))


 
Digitman ©   (2004-07-09 12:41) [29]


> Тимохов ©   (09.07.04 12:34) [27]



> не особо силен в создании машкода да еще готового к употреблению,
> но если бы знал как писать машкод, то делал бы именно так.


занаешь ли, я думаю что редкий мазохист возьмется строить машкод (да еще и позиционно-независимый) в ран-тайм, когда под руками есть мощный/гибкий/изящный Делфи-компилятор, делающий то же самое без каких-либо затрат мозговых усилий))))


 
Тимохов ©   (2004-07-09 12:45) [30]


>  (да еще и позиционно-независимый)

вот об этом подробнее пожалуйста :)))
Как это можно в дельфи сделать?
Если посмотреть в момент выполнени, то видно много явных ссылок, например на константы строк. И как в дельфи сделать позиционно-независимый код?


 
bar   (2004-07-09 13:08) [31]

>>Digitman ©
А для выгрузки дллки как нужно делать? так как ниже или иначе?

function Unload(ProcessID: Cardinal; DllId: Cardinal): Boolean;
var
hProcess, hTh: THandle;
BytesWritten, ThreadID, DllNameLen: Cardinal;
LoadLibraryProc, MemPtr: Pointer;
ExitCode: DWord;
begin
Result := false;

//
// этот вызов нужен только для внедрения в системные процессы
// btw, нужны привилегии администратора
//
SetDebugPriv();

hProcess := OpenProcess(PROCESS_CREATE_THREAD or PROCESS_VM_OPERATION or PROCESS_VM_WRITE,
 true, ProcessID);

if hProcess <> 0 then
begin
 DllNameLen := SizeOf(DllId);

 MemPtr := VirtualAllocEx(hProcess, nil, DllNameLen, MEM_COMMIT, PAGE_READWRITE);
  //   FreeLibrary
      //  FreeLibrary()
 if MemPtr <> nil then
 begin
   if WriteProcessMemory(hProcess, MemPtr, Pointer(DllId), DllNameLen, BytesWritten) then
   begin
     LoadLibraryProc := GetProcAddress(GetModuleHandle("kernel32.dll"), "FreeLibrary");
     hTh := CreateRemoteThread(hProcess, nil, 0, LoadLibraryProc, MemPtr, 0, ThreadID);

     if hTh <> 0 then
     begin
       if (WaitForSingleObject(hTh, INFINITE) = WAIT_OBJECT_0) and
         GetExitCodeThread(hTh, ExitCode) then
         Result := ExitCode <> 0;
       CloseHandle(hTh);
     end;
   end;
   VirtualFreeEx(hProcess, MemPtr, 0, MEM_RELEASE);
 end;
 CloseHandle(hProcess);
end;
end;


 
bar   (2004-07-09 13:16) [32]

>> All
Спасибо, мастера Delphi, знатоки Рихтера и прочие гении.
С CreateRemoteThread разберусь позже а пока сделал чтобы работала загрузка/выгрузка с пом. хука.
Вот процедура хука кому интересно. Можете покритиковать.

function CallWndProc(
   nCode: integer; // hook code
   wParam: WPARAM; // current-process flag
   lParam: LPARAM // address of structure with message data
   ): LRESULT; stdcall;
var
   hl: THAndle;
   ok: boolean;
begin
   if nCode = HC_ACTION then
   begin
       ok := false;
       if tmsg(pointer(lparam)^).hwnd = H then
       begin
           ok := true;
           if GlobalData^.Flag then
           begin
               GlobalData^.HLIB := loadlibrary(remdll);

               @GetAndSet := nil;
               if GlobalData^.HLIB > HINSTANCE_ERROR then
               begin
                   @GetAndSet := GetProcAddress(GlobalData^.HLIB, "GetAndSet");
                   if @GetAndSet <> nil then
                       GetAndSet(H) ;
                   Invalidaterect(H, nil, false);
                  end else

           end else
           begin
{ я заменяю оконную процедуру в SHELLDLL_DefView и по этому сообщению(WM_User + 456) возвращаю на место старую WndProc }
               SendMessage(H, WM_User + 456, 0, 0);
               sleep(1000);{Дабы точно WndProc заменилась на старую. Пожет это параноя :-)}
               hl := loadlibrary(remdll);
               repeat
              until not FreeLibrary(Hl);;
           end;
       end;
       result := CallNextHookex(GlobalData^.SysHook, ncode, wparam, lparam);
       if ok then
       begin
           UnHookWindowsHookEx(GlobalData^.SysHook);
       end
   end
   else
       result := CallNextHookex(GlobalData^.SysHook, ncode, wparam, lparam);
   //

end;
Пока.


 
Digitman ©   (2004-07-09 15:16) [33]


> Тимохов ©   (09.07.04 12:45) [30]


чуть позже


 
Тимохов ©   (2004-07-12 12:06) [34]


> Digitman ©   (09.07.04 15:16) [33]
> чуть позже


Эххх :((
видать, я состарюсь раньше :))))


 
Бином Ньютоныч   (2004-07-12 18:33) [35]

Тимохов ©   (12.07.04 12:06) [34]

Пишешь дважды одну функцию под разными именами, компилишь. Сравнивая код, строишь таблицу коррекции адресов. Так еще под 580ВМ80 делали перемещаемый код.

О kernel32. Все системные библиотеки скомпонованы с неперекрывающимися базовыми адресами. Так что вероятность их загрузки по иному адресу практически равна нулю. Реальный адрес загрузки своей библиотеки можно посмотреть в окне Modules и соответственно скорректировать компоновку. Там же можно посмотреть адрес загрузки kernel32 и сравнить с показаниями TDUMP.exe. Кстати, в Visual Studio есть утилита Rebase.exe, которая позволяет произвести оптимизацию базовых адресов.

PS: Все это описано у Рихтера.


 
Тимохов ©   (2004-07-12 18:46) [36]


> PS: Все это описано у Рихтера.

читал, знаю.
спасибо.

все же именно на мой вопрос ответа нет (т.е. где явно описан факт одинаковости базового адреса загрузки kernel32.dll)


 
Бином Ньютоныч   (2004-07-12 20:35) [37]

>Тимохов ©   (12.07.04 18:46) [36]

Может и есть компоновщики, которые не размещают системные библиотеки первыми в секции импорта, но я о таком не слышал.



Страницы: 1 вся ветка

Текущий архив: 2004.08.22;
Скачать: CL | DM;

Наверх




Память: 0.6 MB
Время: 0.06 c
4-1089277426
Volodya_
2004-07-08 13:03
2004.08.22
CreateProcess


3-1091351123
Koala
2004-08-01 13:05
2004.08.22
Понять не могу....(делаю перенос данных из Dbase в Firebird)


1-1091533799
П7
2004-08-03 15:49
2004.08.22
Умные плагины в DLL


1-1092038278
Goorus
2004-08-09 11:57
2004.08.22
Класс-контейнер


14-1091630890
ram
2004-08-04 18:48
2004.08.22
как в IE поменять адрес домашней страницы