Модераторы: bsa

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Знает ли статическая функция о типе своего обьекта, ? 
:(
    Опции темы
Logo
Дата 15.3.2012, 21:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 694
Регистрация: 22.7.2008

Репутация: 1
Всего: 10



mes
Родительский класс-шаблон содержит базовые методы для всех генерируемых классов. В первую очередь это replacement new. А уже дочерние - остальное - в том числе статические контанты, указывающие, как именно дочерний обьект должен размещатся в памяти, относительно параметра, преданного в replacement new.

Это сообщение отредактировал(а) Logo - 15.3.2012, 21:17
PM MAIL   Вверх
mes
Дата 16.3.2012, 00:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



Logo,  это Вы озвучили решение, а вот задача, которую будет решать этот шаблон все также не ясна.. А без четко поставленной задачи очень трудно будет Вам найти наиболее подходящее решение.. 



--------------------
PM MAIL WWW   Вверх
baldina
Дата 16.3.2012, 09:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 15
Всего: 101



Цитата(Logo @  15.3.2012,  21:17 Найти цитируемый пост)
как именно дочерний обьект должен размещатся в памяти

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

#include <iostream>

class Parent {
protected:
  static long VAR;
public:
  void *operator new(size_t size, void* ptr) {
    std::cout << "VAR "<< VAR;
    return ptr;
  }
};

long Parent::VAR;

class Child : public Parent {
public:
  void *operator new(size_t size, void* ptr) {
    VAR = 10;
    return Parent::operator new(size, ptr);
  }
};
int main() {
  int test;
  void* memory = &test;
  Child *c = new(memory) Child;
}

насколько это серьезно - решать разработчику
PM MAIL   Вверх
Logo
Дата 16.3.2012, 23:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 694
Регистрация: 22.7.2008

Репутация: 1
Всего: 10



Ну, задача довольно специфическая. Пишу бинарный патч к програме. Исходников нет, это abandonware по сути.
Кода довольно много, что бы писать его на чистом ассемблере. Поэтому решил также написать небольшую библиотеку, для написания бинарных патчей.
Одна из распрастраненных задач - вставить в какое-либо место свой блок кода. Идея в том что бы привязать структуру-класс к лексической области видимости функции. Таким образом получаем доступ к типизированным переменным программы (в том числе к разным структурам, которых там дохрена).
Соответственно в терминах ООП это можно было бы описать например так.
Базовый класс, содержащий в себе статический метод размещения объекта в памяти, оператор placement new, например.
Его наследники, классы Scope - структуры, описывающие типы переменных конкретной функции, также содержит статическое integer поле, определяющее, что в структуре является локальными переменными (остальное у нас - сохраненные регистры, адрес возврата, и аргуметы функции).
Наследники этих классов, описывающие конкретный блок кода, который мы хотим добавить в функцию. Содержит в себе внутренний тип - структуру, описывающую и типизирующую регистровые переменные, статические поля - регистр, относительно которого начинается блок локальных переменных в стеке, и смещение. Также может быть будут статические поля - адрес начала и конца блока, что бы записать код вставки блока в код основной программы, либо код все таки будет вставлятся вручную патчем. И метод, содержащий в себе собственно код.
Вставка код на этой библиотеке выглядит примерно так.

Код

void Block0_funcName::run(Block0_funcName::Reg &reg) {
  //поля класса - локальные переменные программы, добавляем свой код здесь
}

__cdecl void block0_funcName(Reg reg) { //фукнция, экспортируемая dll
  Block0_funcName::tieAndRun(reg);      //регистры передаются по ссылке
  // или 
  (new(reg) Block0_funcName)->run(reg);
}

/*
В код программы вставляется
pushad
call block0_funcName
popad
*/


Описание классов блоков кода с локальными и регистровыми переменными выносится в отдельный файл, что упрощает портирование патча под разные сборки программы.
Описание этих классов должно содержать в себе как можно меньше служебного кода, в пользу декларативного описания переменных, регистров и прочего.

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

Код

struct _Scope_funcName {  // лексические переменные функции
  long var1;
  long var2;
  SomeStruct var3;
  BYTE __undefined_0[0x16]; //не описанные переменные
  long var4;
  DWORD s;
  DWORD r;
  long arg1;
  long arg2;
  static const long NOTVARS = 4; //колличество полей струтуры, НЕ являющихся локальными переменными
};
struct _Block0Reg_funName {  // регистровые переменные
  unsigned long edi;       //edi
  unsigned long esi;       //esi
  unsigned long ebp;       //ebp
  unsigned long esp;       //esp
  unsigned long ebx;       //ebx
  unsigned long edx;       //edx
  SomeStruct    rvar1;     //ecx
  long          rvar2;     //eax
}; 
typedef ScopeBlock<
  _Scope_funcName,     //структура локальных переменных, аргументов функции
  _Block0Reg_funName,  //структура регистровых переменных
  /* STORED = */ EBP,  //регистр, относительно которого вычисляется адрес начала блока локальных переменных
  /* SHIFT  = */ 8,    //смещение
  //возможно также:
  /* FROM = */   0x401329,
  /* TO =   */   0x401330,
  /* FUNC = */   block0_funcName //функция, или когда ее можно сделать статической функцией класса, например
> Block0_funcName;
// и, если мы используем динамическую вставку блока, какая-то функция, добавляющая новый класс в массив,
// что бы потом добавить код вставки блока по заданным адресам при загрузке dll

А как бы эту зачу решили бы вы?


А вот тестовая черновая наброска одного из вариантов, не ручаюсь за точность и тем более красоту кода, но он работает). Компилятор MSVC

Код

#include <stdio.h>
#include <stdlib.h>

struct BReg {
  unsigned long edi;
  unsigned long esi;
  unsigned long ebp;
  unsigned long esp;
  unsigned long ebx;
  unsigned long edx;
  unsigned long ecx;
  unsigned long eax;
};

//class Tie

const long EBP = 0x2;
const long ESP = 0x3;

template <class T>
class ScopeBlock {
public:
  typedef BReg Reg;
  //typedef T::Reg SReg;
  static T* tie(void *ptr, long shift) {
    return (T*)((char*)ptr - (sizeof(T) - T::NOTVARS*4) + shift - 4);
  }
  static T* tie(Reg reg) {
    return tie((void *)*((long *)&reg + T::STORED), T::SHIFT);        
  }
 // static T* tie(T::Reg reg) {}
  static void some(Reg reg) {
  }
  void static tieAndRun(void *ptr, long shift, Reg &reg) {

  }
  
  virtual int some()=0;
};

struct Irc_JOIN {
  long one;
  long two;
  long three;
  static const long NOTVARS = 1;    
};
struct BlockIrc_JOIN : public Irc_JOIN, public ScopeBlock<BlockIrc_JOIN>
{
  struct Reg {
    long eax;
    long ebx;    
  };
  void run(Reg &reg);
  static const long STORED = EBP;
  static const long SHIFT  = 0;
};


void BlockIrc_JOIN::run(BlockIrc_JOIN::Reg &reg) {
  printf("one - %x, two - %x, three - %x", one, two, three);
}

//void blockIrc_JOIN(BReg reg) {
 // BlockIrc_JOIN::tie(reg);
//}

void block_test(BReg reg);
void test() { //функция в exe файле
  //Эмулируем long one, two, tree, т.к. компилятор не сохраняет порядок переменных
  long vars[3] = {0x10, 0x20, 0x30};
  __asm {
    mov esi, 55h;
    pushad;
    call block_test;
    popad;
  }
}

void block_test(BReg reg) {
   BlockIrc_JOIN *b = BlockIrc_JOIN::tie(reg);
   //printf("one - %x, two - %x, three - %x", b->one, b->two, b->three);
   BlockIrc_JOIN::Reg r2;
   b->run(r2);
  // BlockIrc_JOIN::some(r2);
}

int main() {
  test();
  return 0;
}



Это сообщение отредактировал(а) Logo - 17.3.2012, 03:00
PM MAIL   Вверх
mes
Дата 17.3.2012, 01:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



весь код не смотрел..

Цитата(Logo @  16.3.2012,  22:21 Найти цитируемый пост)
Идея в том что бы привязать структуру-класс к лексической области видимости функции.

статически ? вот посмотрите пример :

http://liveworkspace.org/code/6703fd7250eb...e4f42b024d09278


--------------------
PM MAIL WWW   Вверх
Logo
Дата 17.3.2012, 02:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 694
Регистрация: 22.7.2008

Репутация: 1
Всего: 10



Спасибо, конечно, за код, но нужно совсем не это, о чем я уже написал выше в своем сообщении.
Надо - написать dll, подгрузить в экзешник эту dll и вставить в нужных местах код обращения к этой dll, например
Код

pushad
call mуFunc
popad

А из C++ уже работать с переменными функции исходной програмы, как с обычными C++ переменными

Это сообщение отредактировал(а) Logo - 17.3.2012, 16:06
PM MAIL   Вверх
Logo
Дата 17.3.2012, 06:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 694
Регистрация: 22.7.2008

Репутация: 1
Всего: 10



А вот более понятный exemple, должен компилится MSVC и GCC 3
Код

#include <stdio.h>

const int EBP = 2;
const int ESP = 3;

//<либа

template <class TVars, class TReg, int TSTORED, long TSHIFT>
class Scope : public TVars {
public:
  typedef TReg Reg;
  void* operator new (size_t size, void *ptr) {
    return ptr;
  }
  void* operator new (size_t size, void *ptr, long shift) {
    return (Scope*)((char*)ptr - (sizeof(Scope) - TVars::NOTVARS*4) + shift );      
  }
  void* operator new (size_t size, Reg &reg) {
    return operator new( sizeof(Scope), (void *)*((long *)&reg + TSTORED), TSHIFT );         
  }
  void run(Reg &reg);
};

//>либа

//<обявление обьекта "блок кода"

struct _Scope_someFunc { //локальные переменные функции
  long one;
  long two;
  long three;
  #ifdef _MSC_VER 
  static const long NOTVARS = 0;
  #endif
  #ifdef __GNUC__
  static const long NOTVARS = -3; //GCC зачем-то добавляет еще 3 лишних DWORD
  #endif
};

struct _Block0Reg_someFunc { //регистровые переменные функции
  unsigned long edi;     //edi
  long          esitest; //esi
  unsigned long ebp;     //ebp
  unsigned long esp;     //esp
  unsigned long ebx;     //ebx
  unsigned long edx;     //edx
  unsigned long ecx;     //ecx
  unsigned long eax;     //eax
};

typedef Scope<
  _Scope_someFunc,
  _Block0Reg_someFunc,
  EBP,  //регистр, относительно которого вычисляется начало блока локальных переменнх
  0     //смещение
> Block0_someFunc;

//>обьявление обьекта "блок кода"

template<> void Block0_someFunc::run(Reg &reg) { //наш добавляемый код
  long &esitest = reg.esitest;
  //работаем с локальными переменными функции
  printf("one - %lx, two - %lx, three - %lx, esitest - %lx", one, two, three, esitest);        
}

void block0_someFunc(Block0_someFunc::Reg reg) { //функция, экспортируемая dll
  (new(reg) Block0_someFunc)->run(reg);
}

void someFunc() { //целевая функция в exe файле
  long vars[3]; //эмулируем long one, two, three, т.к. компилятор может не сохранять порядок переменных
  vars[0] = 0x10; //one   = 0x10;
  vars[1] = 0x20; //two   = 0x20;
  vars[2] = 0x30; //three = 0x30;
  #ifdef _MSC_VER
  __asm {
    mov esi, 55h //esitest
    pushad  //<наша вставка
    call block0_someFunc
    popad   //>наша встака
  };
  #endif
  #ifdef __GNUC__
  asm("mov $0x55, %esi"); //esitest
  asm("pusha"); //<наша вставка
  asm("call %0":"=m"(block0_someFunc));
  asm("popa");  //>наша вставка
  #endif  
}

int main() {
    someFunc();
}


Это сообщение отредактировал(а) Logo - 19.3.2012, 00:13
PM MAIL   Вверх
xvr
Дата 17.3.2012, 10:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 35
Всего: 223



Цитата(Logo @  16.3.2012,  23:21 Найти цитируемый пост)
А как бы эту зачу решили бы вы?

Отделил бы доступ к переменным, блок кода и инжект в exe в разные сущности. Они практически не имеют ничего общего (в статике), все их взаимодействие - чисто динамическое. Так что наследовать друг друга они не должны

PM MAIL   Вверх
mes
Дата 17.3.2012, 11:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



Цитата(Logo @  17.3.2012,  05:05 Найти цитируемый пост)
void __cdecl block0_someFunc(Block0_someFunc::Reg reg) { //функция, экспортируемая dll
  (new(reg) Block0_someFunc)->run(reg);
}

интуиция подсказывает, что тут можно "по-человечески " подойти.. а значит и остальное выпрямится.. 





--------------------
PM MAIL WWW   Вверх
mes
Дата 17.3.2012, 12:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



Logo, немножкo поразмышляю.. 

у нас есть некая функция, в которой мы рыботаем с локальными переменными.. 
мы хотим передать эти переменные и приплюсованные к ним регистры в другую функцию.. 
определяем два типа fn_vars, regs..  они лежат в некотором контехте, указатель на который мы передаем библиотечной функции.. 
тогда, функция в приложении, будет иметь условный вид :
Код

void app_fn
{
     fn_vars vars;

      __asm { pushad 
              call  dll_fn
              popad   
      };
}

тогда библиотечная функция
Код

void dll_fn (fn_context&)
{
   ... 
}

на данный момент на программиста возлагается ответсвеность по правильному отражению контекста. 

пользовательская функция, может иметь два вида, быть как свободной функцией, так и членом класса..
для первого случая
Код

void free_fn (fn_vars, regs)
{
//   do some;
}

void dll_fn (fn_context& ctx)
{
    free_fn (ctx->get_vars (), ctx->get_regs());
}

для второго
Код

class mem_fn
{
    public:
       mem_fn (fn_vars& vars) : _vars(vars) {}
      
        void  operator () (regs& regs)  { ... }
   
    private:
      fn_vars& _vars;
};


void dll_fn (fn_context& ctx)
{
    mem_fn (ctx.get_vars()) (ctx.get_regs());
}


Добавлено @ 12:41
список аргументов мы можем расширить набором внешних переменных : fn_evars..
например две разные dll_fn, вызывают одну и ту же конечную функцию

Код

void free_fn (fn_vars, fn_evars, regs ){}

fn_evars v1;
dll_fn1 (context& ctx)
{
     free_fn (ctx.get_vars(), v1, ctx.get_regs());
}
fn_evars v2;
dll_fn2 (context& ctx)
{
     free_fn (ctx.get_vars(), v2, ctx.get_regs());
}



Это сообщение отредактировал(а) mes - 17.3.2012, 13:52


--------------------
PM MAIL WWW   Вверх
mes
Дата 17.3.2012, 13:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



вот набросок на основе вышеприведенных рассуждений :
  http://liveworkspace.org/code/c657640f3e3e...71a4a90a97b004f


Это сообщение отредактировал(а) mes - 17.3.2012, 13:40


--------------------
PM MAIL WWW   Вверх
Logo
Дата 19.3.2012, 01:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 694
Регистрация: 22.7.2008

Репутация: 1
Всего: 10



Цитата

Отделил бы доступ к переменным, блок кода и инжект в exe в разные сущности. Они практически не имеют ничего общего (в статике), все их взаимодействие - чисто динамическое. Так что наследовать друг друга они не должны

Инжекта динамического скорее всего не будет пока, а так да, верно пожалуй. Инжект же может делаться не только обьекта "блок кода", можно разрешить инжект отдельных функций с asm кодом.
Что до доступа к переменным (описание переменных) и самого кода, то обьект представляет собой локальную область видимости функции, а его метод run - сам код. Они разнесены в разные файлы с целью упростить портирование патча на другие версии программы. Но сам этот обьект может наследовать более базовый обьект "локальная область видмости функции", т.к. в одной функции может быть более одной вставки.

Цитата

определяем два типа fn_vars, regs..  они лежат в некотором контехте, указатель на который мы передаем библиотечной функции.. 

Цитата

Код

void dll_fn (fn_context&)
{
   ... 
}



Контекст суть и есть регистры, передаются они по значению, можно представить ввиде структуры вида
Код

struct Regs {
  DWORD  edi;     //edi
  DWORD  est;     //esi
  DWORD  ebp;     //ebp
  DWORD  esp;     //esp
  DWORD  ebx;     //ebx
  DWORD  edx;     //edx
  DWORD  ecx;     //ecx
  DWORD  eax;     //eax
};

pushad просто толкает все основные регистры в стек. Зная регистр, относительно которого вычисляется адрес блока локальных переменных (EBP или ESP) и смещение, мы цепляем их в программу.
а popad забирает обратно регистры из стека (возможно специально измененные нами). Своего рода трюк, но корректный, т.к. соответствует соглашению cdecl.

С глобальными переменными проще, можно так
Код

namespace exe {
  typedef long TVar;
  TVar &var = *(TVar*) 0x40303A; //адрес переменной
}
using namespace exe;

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

P.S. адаптировал предыдущий пример под GCC 4 и liveworkspace (считает &reg символом копирайта:), поэтому rreg)
http://liveworkspace.org/code/ba083049f7c1...40feef0ac067af8

Добавлено @ 01:54
Цитата(mes @ 17.3.2012,  11:44)
Цитата(Logo @  17.3.2012,  05:05 Найти цитируемый пост)
void __cdecl block0_someFunc(Block0_someFunc::Reg reg) { //функция, экспортируемая dll
  (new(reg) Block0_someFunc)->run(reg);
}

интуиция подсказывает, что тут можно "по-человечески " подойти.. а значит и остальное выпрямится..

Вобще вполне логично, цепляем обьект к памяти, относительно параметра, и выполняем метод run, что здесь не так?

Это сообщение отредактировал(а) Logo - 19.3.2012, 02:19
PM MAIL   Вверх
mes
Дата 19.3.2012, 09:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



Цитата(Logo @  19.3.2012,  00:49 Найти цитируемый пост)
Контекст суть и есть регистры, передаются они по значению, можно представить ввиде структуры вида

контекст в данном случае не только регистры, а и все остальные пуши, которые отражают стек переменных функции.. 
контекст условное обозначение, и можно вместо него принять соглашение о вызове с асма не dll_fn (context&) , a  dll_fn (vars&, regs&) ; в любом случае это отражение ложиться на плечи asm -вызова и нужно выбрать что удобнее...

Цитата(Logo @  19.3.2012,  00:49 Найти цитируемый пост)
Своего рода трюк, но корректный, т.к. соответствует соглашению cdecl.

 smile

Цитата(Logo @  19.3.2012,  00:49 Найти цитируемый пост)
С глобальными переменными проще, можно так

там не глобальные переменные целевого-exe, а примыкание, для передачи дополнительных аргументов..
вполне возможно этого и не нужно.. значит половину можно выкинуть..


Цитата(Logo @  19.3.2012,  00:49 Найти цитируемый пост)
И глобальные переменные одни для всех функций, зачем их еще как-то передавать в каждую функцию?

не в каждую, а только в ту что нужно.. а притягивать, чтоб можно было передавать разные.. т.е. для рабочей функции они не будут глобальными... 
в общем не забивайте этим голову.. 

Цитата(Logo @  19.3.2012,  00:49 Найти цитируемый пост)

Вобще вполне логично, цепляем обьект к памяти, относительно параметра, и выполняем метод run, что здесь не так?

логично было бы, если б вместо "цепляем" было "кастим" (cast)..




--------------------
PM MAIL WWW   Вверх
mes
Дата 19.3.2012, 10:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



Цитата(Logo @  19.3.2012,  00:49 Найти цитируемый пост)
P.S. адаптировал предыдущий пример под GCC 4 и liveworkspace

ловите под напильник :
http://liveworkspace.org/code/4cf655d26a6b...b2fa1a75708e607

Добавлено @ 10:44
P.S. расчет адреса (в invoke) требует поправки.. 


Это сообщение отредактировал(а) mes - 19.3.2012, 11:49


--------------------
PM MAIL WWW   Вверх
mes
Дата 19.3.2012, 12:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



там вообще то еще проще получается, убрал лишнее : 
http://liveworkspace.org/code/ce5656743414...b1f582a9454735b

пример работы с локальными переменными, как с объектом :
http://liveworkspace.org/code/d0b846f4046a...7f5ed1c7f308b8b

Это сообщение отредактировал(а) mes - 19.3.2012, 12:26


--------------------
PM MAIL WWW   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.0750 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.