Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2008.06.15;
Скачать: [xml.tar.bz2];

Вниз

Тонкости перехвата API   Найти похожие ветки 

 
Dmitry S ©   (2008-04-28 22:59) [0]

Конструктивно:

Перехватываю функцию CreateFileW путем записи нескольких первых байт. (точнее 10):
$68, $A1, $A2, $A3, $A4 // push A1A2A3A4h
$68, [@MyCreateFileW] // push ...
$C3 // ret

После чего, как я думал, мне необходимо в описание MyCreateFileW добавить еще один параметр (первый) резмером Integer, где и будет храниться переданное мной число A1A2A3A4h.
Однако параметр полченный в функцию "левый", а число A1A2A3A4h продолжает храниться в стеке. Почему так? Ведь я добавил еще один параметр, он ведь должен браться из стека?


 
Тыщ ©   (2008-04-28 23:04) [1]

Какое соглашение вызова у функции MyCreateFileW?


 
Dmitry S ©   (2008-04-28 23:04) [2]

stdcall


 
Dmitry S ©   (2008-04-28 23:11) [3]

А еще вопрос по теме.
Как сделать call по абслютному адресу? Т.е. в машинных инстукциях как?
E8 xx xx xx xx - это я так понимаю относительный. А как абсолютный?


 
Тыщ ©   (2008-04-28 23:15) [4]

Dmitry S ©   (28.04.08 23:11) [3]

call по абслютному адресу сделать нельзя, потому так как он изменит сегментный регистр cs, а это запрещено в Windows.
Да и не нужно, в общем-то.


 
Dmitry S ©   (2008-04-28 23:19) [5]

А как тогда быть??


 
Тыщ ©   (2008-04-28 23:19) [6]

mov eax, some_absolute_address
call eax


 
Dmitry S ©   (2008-04-28 23:23) [7]

окей.
А можно это в кодах команд?


 
Тыщ ©   (2008-04-28 23:25) [8]

B8 78563412 // mov eax, 12345678h
FFD0 // call eax


 
Dmitry S ©   (2008-04-28 23:43) [9]

Не понимаю вот чего:

Делаю простой вызов своей
MyCreateFileW

В итоге в окне асемблера наблюдаю:
делается куча push, потом call
а в теле функци pop-ов нет, значения переменных беруться из памяти. как так?


 
Anatoly Podgoretsky ©   (2008-04-28 23:52) [10]

> Dmitry S  (28.04.2008 23:43:09)  [9]

Есть такое понятие, как фрейм стека, вот отностительно его и берутся.
Сходи ко мне на сайт и возьми две статьи по basm

http://www.podgoretsky.com/Redir.aspx?id=142&DownloadFile=~/ftp/Docs/Delphi/Podgoretsky/basmru.zip
http://www.podgoretsky.com/Redir.aspx?id=129&DownloadFile=~/ftp/Docs/Delphi/Podgoretsky/BasmForBeginners-ru.doc

Поскольку ты занимаешься АСМ то информация будет интересна.


 
Dmitry S ©   (2008-04-29 00:11) [11]

Кажеться я понял в чем дело.
Объясните пожалуйста следующее.
Мне нужно вставить некое значение в стек ПЕРЕД текущим. Так правильно:
pop eax;
push $xxxxxxx;
push eax;
?


 
Dmitry S ©   (2008-04-29 00:21) [12]

Все!!! Я понял!! И исправил.

Дело в том, что я "добавлял" этот самый параметр ПОСЛЕ выполнения call. А последняя использует стек, видимо для хранения точки возврата. Поэтому мой параметр и адрес точки возврата, какбы поменялись местами.Вставил код из 11, все заработало =) уфф=)


 
Dmitry S ©   (2008-04-29 00:40) [13]

В итоге получился такой код:

pop eax  // 58
push $xxxxxxxx // 68xxxxxxxx
push eax  // 50
push $yyyyyyyy // 68yyyyyyyy
ret  // c3

Этот код я вставляю в начало перехватываемой функции.
Тут $xxxxxxxx - адрес какого либо параметра
$yyyyyyyy - мой функция, которая должна выполнить оригинальную...
Блин, совсем как с SetIntVec в паскале =)


 
KSergey ©   (2008-04-29 09:55) [14]

> Dmitry S ©   (29.04.08 00:21) [12]
> ПОСЛЕ выполнения call.
> А последняя использует стек, видимо для хранения точки возврата.

А может книжки-то почитать, а?
Ну зачем постигать компьютер методом проб и ошибок? Это ж жизни не хватит!


 
Rouse_ ©   (2008-04-29 10:18) [15]

Что-то ты сурово как-то сплайсинг делаешь... Вот тебе навскидку примерчик от руки написанный... Должен работать по идее

type
 TSpliceRec = packed record
   JMP_OpCode: Byte;
   ProcAddr: Pointer;
   RET_OpCode: Byte;
 end;

var
 FuncAddr: Pointer;
 OldSpliceData, NewSpliceData: TSpliceRec;

function SpliceEntry(const FuncAddr: Pointer;
 const NewData: TSpliceRec): Boolean;
var
 Dumme: DWORD;
begin
 Result := WriteProcessMemory(GetCurrentProcess, FuncAddr, @NewData,
   SizeOf(TSpliceRec), Dumme);
 if Result then
   Result := Dumme = SizeOf(TSpliceRec);
end;

type
 TCreateFileA = function(lpFileName: PAnsiChar;
   dwDesiredAccess, dwShareMode: DWORD;
   lpSecurityAttributes: PSecurityAttributes;
   dwCreationDisposition, dwFlagsAndAttributes: DWORD;
   hTemplateFile: THandle): THandle; stdcall;

function InterceptedCreateFileA(lpFileName: PAnsiChar;
 dwDesiredAccess, dwShareMode: DWORD;
 lpSecurityAttributes: PSecurityAttributes;
 dwCreationDisposition, dwFlagsAndAttributes: DWORD;
 hTemplateFile: THandle): THandle; stdcall;
begin
 SpliceEntry(FuncAddr, OldSpliceData);
 dwDesiredAccess := GENERIC_READ;
 dwShareMode := FILE_SHARE_READ or FILE_SHARE_WRITE;
 dwCreationDisposition := OPEN_EXISTING;
 Result := TCreateFileA(FuncAddr)(lpFileName, dwDesiredAccess,
   dwShareMode, lpSecurityAttributes, dwCreationDisposition,
   dwFlagsAndAttributes, hTemplateFile);
 SpliceEntry(FuncAddr, NewSpliceData);
end;


пример вызова:

 NewSpliceData.JMP_OpCode := $68;
 NewSpliceData.ProcAddr := @InterceptedCreateFileA;
 NewSpliceData.RET_OpCode := $C3;
 FuncAddr := GetProcAddress(GetModuleHandle(kernel32), "CreateFileA");
     ReadProcessMemory(GetCurrentProcess,
     FuncAddr,
     @OldSpliceData, SizeOf(TSpliceRec), Dumme);

 SpliceEntry(FuncAddr, NewSpliceData);
 try
   M := TFileStream.Create(Path, fmOpenRead);
   try
     M.Position := LogRecord[Index].Rec.DataOffset;
     Stream.CopyFrom(M, LogRecord[Index].Rec.DataSize);
     Result := True;
   finally
     M.Free;
   end;
 finally
   SpliceEntry(FuncAddr, OldSpliceData);
 end;


 
Дмитрий С   (2008-04-29 10:26) [16]


> Rouse_ ©   (29.04.08 10:18) [15]

Я делаю тоже самое, только передаю в InterceptedCreateFileA дополнительный параметр. Типа LPVOID lpParameter у CreateThread.


 
Дмитрий С   (2008-04-29 10:28) [17]

Вечером скину код, что получилось. Он дома.

ЗЫ. Не удается с тобой наладить связь. =(


 
Дмитрий С   (2008-04-29 10:29) [18]


> А может книжки-то почитать, а?
> Ну зачем постигать компьютер методом проб и ошибок? Это
> ж жизни не хватит!

Так ведь это просто! Мне АСМ в принципе не нужен. Для таких нехитрых задач достаточно тех знаний что есть:) Конечно возникают вопросы. Но они у всех возникают


 
Дмитрий С   (2008-04-29 10:35) [19]


> Поскольку ты занимаешься АСМ то информация будет интересна.
>
>

благодарю! :)


 
Rouse_ ©   (2008-04-29 10:38) [20]


> ЗЫ. Не удается с тобой наладить связь. =(

Дай свою асю сам свяжусь, у меня антиспам стоит, он почемуто только с QIP пропускает клиентов :)


 
Дмитрий С   (2008-04-29 10:40) [21]

Удалено модератором


 
Сергей М. ©   (2008-04-29 15:53) [22]


> Dmitry S ©   (28.04.08 22:59)


А нафих тебе сплайсинг сдался ?
Чем не устроил перехват методом модификации EAT+IAT ?


 
Дмитрий С   (2008-04-29 16:06) [23]

О вкусах не спорят


 
Сергей М. ©   (2008-04-29 17:08) [24]


> О вкусах не спорят


Не спорят, да)

А вот о блажи можно и поспорить.


 
Rouse_ ©   (2008-04-29 17:18) [25]

Через EAT+IAT доп.параметр не перекинешь, хотя я так и не понял зачем он тут сдался :)


 
Сергей М,   (2008-04-29 19:47) [26]


> зачем он тут сдался


Как зачем ? Блажь !) Самая что ни на есть настоящая).. Надежно прикрытая "вкусами, о которых не спорят")


 
Тыщ ©   (2008-04-29 22:47) [27]

Anatoly Podgoretsky ©   (28.04.08 23:52) [10]

> basmru.zip

Почитал. Что-то опечаток многовато, и некоторые неточности имеются.


 
Dmitry S ©   (2008-04-29 23:20) [28]

unit InterceptAPI;

interface
uses Windows;

type
 TInterceptProc = packed record
   instr_pop1: byte;               // pop eax;
   instr_push1: byte;              // push InterceptStruct;
   InterceptStruct: Pointer;
   instr_push2: byte;              // push eax;
   instr_push3: byte;              // push address;
   address: Pointer;
   instr_ret: byte;                // ret;
 end;

 PInterceptStruct = ^TInterceptStruct;

 TInterceptStruct = record
   OriginalFunction: Pointer;
   NewFunction: Pointer;

   Lock:RTL_CRITICAL_SECTION;

   NewFunctionCode: TInterceptProc;
   OldFunctionCode: TInterceptProc; // тип используется лишь для сорaзмерности
 end;

{ InterceptFunction устанавливает перехват функции по адресу OriginalFunction
функцией по адресу NewFunction. Под структуру InterceptStruct выделять память не нужно -
выделяется автоматически.
Важно!
Функции OriginalFunction и NewFunction должны быть stdcall.
Причем NewFunction имеет не только те же параметры, что и OriginalFunction, но
и дополнительный первый параметр: InterceptStruct: PInterceptStruct.
Например оригинальная функция:
 function MessageBoxW(hWnd: HWND; lpText, lpCaption: PWideChar; uType: UINT): Integer; stdcall;
тогда новая функция должна иметь вил:
 function InterMessageBoxW(InterceptStruct: PInterceptStruct; hWnd: HWND; lpText, lpCaption: PWideChar; uType: UINT): Integer; stdcall;

Данный параметр используется для вызовов LockFunction и UnLockFunction.
}
procedure InterceptFunction(var InterceptStruct: PInterceptStruct; const OriginalFunction, NewFunction: Pointer); stdcall;

{
 UnInterceptFunction восстанавливает оригинальную функцию и освобождает память
 стурктуры InterceptStruct.
}
procedure UnInterceptFunction(var InterceptStruct: PInterceptStruct); stdcall;

{
 LockFunction временно восстанавливает функцию.
}
procedure LockFunction(InterceptStruct: PInterceptStruct); stdcall;

{
 UnLockFunction снова перехватывает функцию
}
procedure UnLockFunction(InterceptStruct: PInterceptStruct); stdcall;

implementation

procedure InterceptFunction(var InterceptStruct: PInterceptStruct; const OriginalFunction, NewFunction: Pointer); stdcall;
var
 CurrentProcess: THandle;
 C: DWord;
 I: Integer;
 P:Pointer;
begin
 GetMem(InterceptStruct, SizeOf(TInterceptStruct));

 InterceptStruct^.OriginalFunction := OriginalFunction;
 InterceptStruct^.NewFunction := NewFunction;

 InitializeCriticalSection(InterceptStruct^.Lock);

 InterceptStruct^.NewFunctionCode.instr_pop1 := $58; // pop eax
 InterceptStruct^.NewFunctionCode.instr_push1 := $68; // push
 InterceptStruct^.NewFunctionCode.InterceptStruct := InterceptStruct;

 InterceptStruct^.NewFunctionCode.instr_push2 := $50; // push eax

 InterceptStruct^.NewFunctionCode.instr_push3 := $68; // push
 InterceptStruct^.NewFunctionCode.address := NewFunction;
 InterceptStruct^.NewFunctionCode.instr_ret := $C3; // ret

 CurrentProcess := GetCurrentProcess;
 ReadProcessMemory(CurrentProcess, OriginalFunction, @InterceptStruct^.OldFunctionCode, SizeOf(TInterceptProc), C);
 WriteProcessMemory(CurrentProcess, OriginalFunction, @InterceptStruct^.NewFunctionCode, SizeOf(TInterceptProc), C);
end;

procedure UnInterceptFunction(var InterceptStruct: PInterceptStruct); stdcall;
var
 C: DWord;
begin
 WriteProcessMemory(GetCurrentProcess, InterceptStruct^.OriginalFunction, @InterceptStruct^.OldFunctionCode, SizeOf(TInterceptProc), C);
 DeleteCriticalSection(InterceptStruct^.Lock);
 FreeMem(InterceptStruct);
end;

procedure LockFunction(InterceptStruct: PInterceptStruct); stdcall;
var
 C: DWord;
begin
 EnterCriticalSection(InterceptStruct^.Lock);
 WriteProcessMemory(GetCurrentProcess, InterceptStruct^.OriginalFunction, @InterceptStruct^.OldFunctionCode, SizeOf(TInterceptProc), C);
end;

procedure UnLockFunction(InterceptStruct: PInterceptStruct); stdcall;
var
 C: DWord;
begin
 WriteProcessMemory(GetCurrentProcess, InterceptStruct^.OriginalFunction, @InterceptStruct^.NewFunctionCode, SizeOf(TInterceptProc), C);
 LeaveCriticalSection(InterceptStruct^.Lock);
end;

end.


Вот код который я создал.
Тут у меня самого замечание.
EnterCriticalSection и LeaveCriticalSection стоят непоназначению.
Как бы сделать, чтобы когда я восстановил функцию, то все ее вызовы из других потоков блокировались EnterCriticalSection. И эта блокировка снималась тогда, когда я восстанавливал функцию?


 
Dmitry S ©   (2008-04-29 23:23) [29]

Неужели придеться действительно через таблицу импорта делать.


 
Сергей М. ©   (2008-04-30 09:02) [30]


> Неужели придеться действительно через таблицу импорта делать.
>
>


И не только "через" нее.
Потребуется еще и модификация таблицы экспорта.

Но этот способ ничуть не хуже сплайсинга и при прочих равных условиях имеет очевидное преимущество в простоте реализации и использования.


 
Дмитрий С   (2008-04-30 10:11) [31]

*ушел делать rtfm* =)


 
Дмитрий С   (2008-05-02 20:26) [32]


> Потребуется еще и модификация таблицы экспорта.

А это зачем?

Я нашел пример в сети. Понял, что с помощью подмены таблицы импорта можно "перекрыть" функции только в своем модуле.
Т.е. если функция вызывается из другого модуля (из какой либо подключенной dll), то вызывается оригинальная функция, а не перекрытая.

Получается, что нужно заранее загружать нужную библиотеку и выполнять перекрытие уже в ней?


 
Rouse_ ©   (2008-05-03 15:20) [33]


> Получается, что нужно заранее загружать нужную библиотеку
> и выполнять перекрытие уже в ней?

Это ко всем случаям относится, если хочешь делать глобально, а не только в своем адресном пространстве...


 
Дмитрий С   (2008-05-04 10:11) [34]


> Это ко всем случаям относится, если хочешь делать глобально,
>  а не только в своем адресном пространстве...

Как же так? Вышеприведенный способ (с помощью сплайсов) не требовал этого. Тем более во время отладки я не раз прерывал программу после перезаписи нескольких байт — и ничего, система жила. И при этом у подгруженной библиотеки функции автоматически были перекрыты.


 
Rouse_ ©   (2008-05-04 10:24) [35]


> Как же так? Вышеприведенный способ (с помощью сплайсов)
> не требовал этого.

Что значит не требовал? Т.е. ты хочешь сказать, что в своем адресном пространстве ты сделал сплайсинг входной точки функции и это изменение автоматически спроецировалось на всю систему?


 
Дмитрий С   (2008-05-04 10:44) [36]


> Что значит не требовал? Т.е. ты хочешь сказать, что в своем
> адресном пространстве ты сделал сплайсинг входной точки
> функции и это изменение автоматически спроецировалось на
> всю систему?

Нет. Только на мой процесс. На все подключенные позже библиотеки (к моему процессу) перехват также распространялся.

А вот способ изменения IAT не распространяется на библиотеки вообще. Т.е. каждую библиотеку нужно хакать индивидуально.


 
Rouse_ ©   (2008-05-04 12:12) [37]


> А вот способ изменения IAT не распространяется на библиотеки
> вообще

Естественно, IAT ты же поправил у своего приложения, а не у библиотеки, тем более это актуально только для статической линковки функций, для динамической применяют правку EAT у библиотеки, хранящей функцию, и это будет справедливо для всех вызовов даной функции из любых модулей, загруженных в твое ВАП (опять же с учетом динамики, а не статики) :)


 
Дмитрий С   (2008-05-04 12:40) [38]

Вот оно что. Ясно.
Если вернуться к той же CreateFileW. Изменения в EAT придеться делать для Kernel32, а как это скажется на работу других процессов.
Честно говоря я не знаю точно сколько копий kernel32 храниться в памяти: одна на всех или по одной на каждый процесс.

ЗЫ. помоему для моей задачи я скоро начну изучать тему "как написать драйвер файловой системы" или типа того=)


 
Rouse_ ©   (2008-05-04 12:53) [39]


> Изменения в EAT придеться делать для Kernel32, а как это
> скажется на работу других процессов.

Никак не скажется, они об этом даже не узнают ибо:

> я не знаю точно сколько копий kernel32 храниться в памяти:
>  одна на всех или по одной на каждый процесс

образ маппится в ВАП твого процесса

> как написать драйвер файловой системы

а это уже из серии "из пушки по воробьям"...


 
oxffff ©   (2008-05-04 14:54) [40]


> Rouse_ ©   (04.05.08 12:53) [39]
>
> > Изменения в EAT придеться делать для Kernel32, а как это
>
> > скажется на работу других процессов.
>
> Никак не скажется, они об этом даже не узнают ибо:
>
> > я не знаю точно сколько копий kernel32 храниться в памяти:
>
> >  одна на всех или по одной на каждый процесс
>
> образ маппится в ВАП твого процесса
>
> > как написать драйвер файловой системы
>
> а это уже из серии "из пушки по воробьям"...


Ничего не мешает мапить разные ВАП на одно ФП.
А именно мапить кодовые сегменты PE образа.


 
Rouse_ ©   (2008-05-04 15:00) [41]


> Ничего не мешает мапить разные ВАП на одно ФП.

Ну тонкостей я уже не помню, но вроде как всеже не кусок ВАП мапится на образ а наоборот, хотя могу и ошибаться...


 
oxffff ©   (2008-05-04 16:07) [42]


> Rouse_ ©   (04.05.08 15:00) [41]


Ничего не мешает кодовую часть PE образа сделать разделяемой для системых DLL, т.е. избежать дублирования кода DLL в физической памяти и файле подкачки, хотя бы для системых библиотек.
И например запретить выгрузку из памяти, аля nonpaged pool, чтобы не создавать нагрузки на другие процессы при переключении.
Хотя это смахивает больше на режим ядра. :)


 
Игорь Шевченко ©   (2008-05-04 16:19) [43]

Я для своего процесса использую следующий метод:
(на примере функций из User и Gdi)

type
 TGetWindowRectProc = function (Wnd: HWND; Rect: PRect): Integer; stdcall;

type
 TInterceptHookData = packed record
   JmpOpCode : Byte;
   JmpAddress : Pointer;
   RetOpCode : Byte;
 end;
 TInterceptData = packed record
   Hook : TInterceptHookData;
   SaveCode : array[0..5] of Byte;
   ProcAddress : Pointer;
   Protection : DWORD;
 end;

procedure InitInterceptData (var InterceptData : TInterceptData;
  HookProc : Pointer);
begin
 with InterceptData.Hook do
 begin
   JmpOpCode := $68;
   JmpAddress := HookProc;
   RetOpCode := $C3;
 end;
end;

function InitInterceptDataEx (var InterceptData : TInterceptData;
  HookProc: Pointer; const AModuleName, AProcName: string): Boolean;
begin
 InitInterceptData(InterceptData, HookProc);
 InterceptData.ProcAddress := GetProcAddress(
   GetModuleHandle(PChar(AModuleName)), PChar(AProcName));
 Result := InterceptData.ProcAddress <> nil;
 if Result then begin
   VirtualProtect(InterceptData.ProcAddress, SizeOf(InterceptData.SaveCode),
      PAGE_EXECUTE_WRITECOPY, InterceptData.Protection);
   Move(InterceptData.ProcAddress^, InterceptData.SaveCode,
      SizeOf(InterceptData.SaveCode));
   Move(InterceptData.Hook, InterceptData.ProcAddress^,
      SizeOf(InterceptData.Hook));
 end;
end;

procedure DoneInterceptData (const InterceptData: TInterceptData);
var
 Dummy : DWORD;
begin
 with InterceptData do
   if Assigned(ProcAddress) then
   begin
     VirtualProtect(ProcAddress, SizeOf(SaveCode), PAGE_EXECUTE_WRITECOPY, Dummy);
     Move(SaveCode, ProcAddress^, SizeOf(SaveCode));
     VirtualProtect(ProcAddress, SizeOf(SaveCode), Protection, Dummy);
   end;
end;

Сам перехватчик (вполне конкретная функция)

var
 GetWindowRectHook: TInterceptData;

function HookedGetWindowRect (Wnd: HWND; Rect: PRect): Integer; stdcall;
var
 Callee: LongInt;
 OldProc: TGetWindowRectProc;
 CallString: string;
begin
 asm
   push dword ptr [ebp+4]
   pop  Callee
 end;
 Inc(Level);
 CallString := Format("GetWindowRect;(%s,%s);[%s]",
   [DecodeWindowHandle(Wnd),DecodeRectPtr(Rect), DecodeAddress(Callee)]);
 with GetWindowRectHook do
 begin
   Move(SaveCode, ProcAddress^, SizeOf(SaveCode));
   @OldProc := ProcAddress;
   Result := OldProc(Wnd,Rect);
   Move(Hook, ProcAddress^, SizeOf(Hook));
 end;
 if LogOpened then
   writeln(LogF, Level, ";",Format("%s;returns %s (%s)", [CallString,
     DecodeBool(Result), DecodeRectPtr(Rect)]));
 Dec(Level);
end;

initialization
 if EnableHooks and (Win32Platform = VER_PLATFORM_WIN32_NT) then
 begin
   InitInterceptDataEx(GetWindowRectHook, @HookedGetWindowRect,
     "user32.dll", "GetWindowRect");
   .....
 end;
finalization
 if EnableHooks and (Win32Platform = VER_PLATFORM_WIN32_NT) then
 begin
   DoneInterceptData(GetWindowRectHook);
   ...
 end;
end.

Пользуюсь уже много лет, а именно 6, в однопоточных приложениях


 
Сергей М. ©   (2008-05-04 17:15) [44]


> Дмитрий С   (02.05.08 20:26) [32]


Модифицировать нужно IAT каждого из модулей в ВАП твоего (т.е. текущего) процесса, ибо любой из этих модулей может вызвать интересующую тебя ф-цию, взяв адрес ее точки входа из своей собственной IAT.

Кр.того, получение адреса интересующей тебя ф-ции возможно и вызовом ф-ции kernel32.GetProcAddress, которая обращается к EAT модуля, экспортирующего перехватываемую тобой ф-цию. Значит, требуется либо модифицировать соотв.элемент EAT либо модифицировать IAT модуля kernel32 с целью перехвата т.входа в GetProcAddress, чтобы отслеживать запросы адреса перехватываемой тобой ф-ции и возвращать результатом нужный тебе адрес.



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

Форум: "Прочее";
Текущий архив: 2008.06.15;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.59 MB
Время: 0.008 c
2-1210734205
azamatufa
2008-05-14 07:03
2008.06.15
EOSError with message A call to an OS function failed


15-1209886588
Ega23
2008-05-04 11:36
2008.06.15
Вэкап-файлы (те которые с ~)


15-1205425934
Lio2007
2008-03-13 19:32
2008.06.15
Delphi 2007. Глюк с контекстным меню в IDE.


2-1211388025
Favorit
2008-05-21 20:40
2008.06.15
InterBase


9-1171128545
electroman239
2007-02-10 20:29
2008.06.15
Алгоритм удаления шаров





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский