Кодерские tips and tricks

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by TaNkist, 16 Nov 2006.

  1. TaNkist

    TaNkist Elder - Старейшина

    Joined:
    6 Apr 2006
    Messages:
    147
    Likes Received:
    47
    Reputations:
    19
    Кодерские tips’n’tricks

    В данной теме делимся программерскими трюками. Это могут быть советы по оптимизации, нестандартные приемы, интересные (но не слишком большие) куски кода. Не стоит превращать тему в свалку исходников, выбирайте наиболее интересные и полезные участки кода. Желательно откомметировать трюк, чтобы всем было понятно.
    Delphi
    Оптимизация​

    [Передача аргументов] Никогда не передавай функции в качестве параметра структуру, лучше передавать указатель на нее
    Code:
    //Ни в коем случае не делай так.
    Procedure code (Data:TStructure);
    //Правильный вариант
    Procedure code (PData:PStructure); //гда PStructure = ^TStrucrure
    
    Старайся передавать своим функциям не более трех параметров, т.к. они передаются через регистры (по соглашению fastcall, принятом по умолчанию в Delphi), а все остальные через стек.
    [Функции - инварианты] Довольно распространенная ошибка программистов – присутствие функций - инвариантов в цикле.
    Code:
    //Неоптимизированный вариант
    While i<= lstrlen(str) do
    Begin
    X:=x+ord(str[i]);
    Inc(i);
    End;
    
    Очевидно, что длина str не меняется, но компилятор считает, что все, что передается по ссылке сожжет быть изменено, и lstrlen вычисляется много раз. Оптимизированный вариант выглядит так.
    Code:
    //Так лучше
    N:=lstrlen(str);
    While i<= n do
    Begin
    X:=x+ord(str[i]);
    Inc(i);
    End;
    
    [Экономия памяти] Когда класс располагается в памяти, то между полями появляются пустые ячейки памяти. Это происходит потому, что Delphi, оптимизируя код, каждое поле располагает от предыдущего со сдвигом в 4 байта.
    Code:
    // Неоптимизированный вариант
    TMyClass = class
      private
        field1: boolean;//1 байт
        field2: longint; //4 байт
        field3: char; //1 байт
        field4: string; //4 байт
        field5: byte; //1 байт
        field6: integer; //4 байт
        field7: byte; //1 байт
      public
        procedure code;
    end;
    
    Реально этот экземпляр класса будет занимать 28 байт. Если мы изменим порядок полей, то сможем добиться уменьшения размера до 16 байт. В нашем примере после field1 размером 1 байт идет field2 размером 4 байта, значит, мы теряем 3 байта на выравнивание. Если же размер field2 не превышал 3 байт, то Delphi не стал бы выравнивать, а поместил бы это поле сразу после первого.
    Code:
    // Оптимизированный вариант
    TMyClass = class
      private
        field1: boolean; //1 байт
        field3: char; //1 байт
        field5: byte; //1 байт
        field7: byte; //1 байт
       field2: longint; //4 байт
        field4: string; //4 байт
        field6: integer; //4 байт
      public
        procedure code;
    end;
    
    [Компиляция без RTL (Run Time Library)] Как известно минимальный размер скомпилированной в Delphi программы с настройками по умолчанию равен 13,5 Кб. Виной тому принудительно подключаемая Delphi RTL, в которой реализованы некоторые возможности языка Delphi.
    Для уменьшения размера скомпилированных прог исправим модели System.pas и SysInit.pas, удалив все «лишнее». Затем перекомпилируем их и полученные dcu-файлы поместим в папку с прогой.
    Минимальный System.pas
    Code:
    unit System;
    
    interface
    
    procedure _HandleFinally;
    
    type
     TGUID = record
      D1: LongWord;
      D2: Word;
      D3: Word;
      D4: array [0..7] of Byte;
     end;
    
     PInitContext = ^TInitContext;
     TInitContext = record
        OuterContext:   PInitContext;   
        ExcFrame:       Pointer;          
        InitTable:      pointer;     
        InitCount:      Integer;          
        Module:         pointer;       
        DLLSaveEBP:     Pointer; 
        DLLSaveEBX:     Pointer; 
        DLLSaveESI:     Pointer;   
        DLLSaveEDI:     Pointer;   
        ExitProcessTLS: procedure;     
        DLLInitState:   Byte;             
     end;
    
    implementation
    
    procedure _HandleFinally;
    asm
    end;
    
    end.
    
    Минимальный SysInit.pas
    Code:
    unit SysInit;
    
    interface
    procedure _InitExe;
    procedure _halt0;
    procedure _InitLib(Context: PInitContext);
    
    var
      ModuleIsLib: Boolean;        
      TlsIndex: Integer = -1;       
      TlsLast: Byte; 
    
    const
      PtrToNil: Pointer = nil;   
    
    implementation
    
    procedure _InitLib(Context: PInitContext);
    asm
    end;
    
    procedure _InitExe;
    asm
    end;
    
    procedure _halt0;
    asm
    end;
    end.
    
    Компиляция
    Code:
    Dcc32.exe – Q System.pas SysInit.pas –M –Y –Z -$D- -O
    
    Некоторые трюки

    [Обмен] Обмен значений между двумя переменными без привлечения третьей.
    Code:
    x:=x xor y;
    y:=y xor x;
    x:=x xor y;
    
     
    2 people like this.
  2. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Оптимизация выходного файла в Microsoft Visual C++.
    Параметры линкера для уменьшения размера:
    - объединение секций
    Code:
    /MERGE:.data=.text /MERGE:.rdata=.text
    - можно еще снизить выравнивание, например, задать 512 байт или 32 байта
    Code:
    /ALIGN:32
    или
    Code:
    /ALIGN:512
    соответственно.
    - смена точки входа
    Code:
    /ENTRY:main
    или
    Code:
    /ENTRY:WinMain
    для консольного и виндового приложения соответственно. Уменьшает размер, т.к. тогда линкер не пихает в экзешник код стартовой функции mainCRTStartup (WinMainCRTStartup). Только надо помнить, что параметры main (WinMain) в этом случае не будут нести никакого смысла, аргументы командной строки придется получать явно через API GetCommandLine().
    - еще можно убрать нафиг CRT или линковать ее динамически:
    Code:
    /NODEFAULTLIB msvcrt.lib
    С учетом всех рекомендаций:
    Code:
    #include <windows.h>
    #pragma comment(linker, "/NODEFAULTLIB /MERGE:.data=.text /MERGE:.rdata=.text /ALIGN:512 /ENTRY:WinMain")
    
    int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
    {
    	MessageBox(0, "Hello, World!", "Tiny application", MB_ICONINFORMATION);
    	return 0;
    }
    
    Результат - имеет хелловорлд размером 1к, состоящим из заголовка экзешника и одной секции.
    Дизассемблерный листинг точки входа не содержит ни одного лишнего байта:
    Code:
    00400230 >/$ 55             PUSH EBP
    00400231  |. 8BEC           MOV EBP,ESP
    00400233  |. 6A 40          PUSH 40                                  ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
    00400235  |. 68 08024000    PUSH tiny.00400208                       ; |Title = "Tiny application"
    0040023A  |. 68 1C024000    PUSH tiny.0040021C                       ; |Text = "Hello, World!"
    0040023F  |. 6A 00          PUSH 0                                   ; |hOwner = NULL
    00400241  |. FF15 00024000  CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA
    00400247  |. 33C0           XOR EAX,EAX
    00400249  |. 5D             POP EBP
    0040024A  \. C2 1000        RETN 10
    
    UPD: Можно снизить выравнивание до 16-и байт, притворившись, что мы собираем драйвер:
    /ALIGN:16 /DRIVER
    это нужно вписать в настройки проекта, в #pragma comment(linker) это не прокатит
     
    #2 _Great_, 16 Nov 2006
    Last edited: 8 Dec 2006
    3 people like this.
  3. TaNkist

    TaNkist Elder - Старейшина

    Joined:
    6 Apr 2006
    Messages:
    147
    Likes Received:
    47
    Reputations:
    19
    Delphi
    Чтобы во время выполнения циклов, не происходил эффект зависания, нужно в теле цикла вставить
    Code:
    Application.ProcessMessages;
    
    Эффект зависания происходит из-за того, что Windows ждет пока накопятся задания в очереди, а не выполняет их сразу. Application.ProcessMessages заставляет винду выполнить все задачи, которые накопились в данный момент.
     
  4. W!z@rD

    W!z@rD Борец за русский язык

    Joined:
    12 Feb 2006
    Messages:
    973
    Likes Received:
    290
    Reputations:
    43
    в VCL (delphi) есть "перехват" закрытия формы
    OnCloseQuery (где canclose boolean типа который и дает "разрешение" на закрытие формы)...
     
    #4 W!z@rD, 19 Nov 2006
    Last edited: 16 Oct 2009
  5. TaNkist

    TaNkist Elder - Старейшина

    Joined:
    6 Apr 2006
    Messages:
    147
    Likes Received:
    47
    Reputations:
    19
    Экстремально маленький Hello Word! на Delphi.

    Собирать исполняемый файл линкером от Microsoft. К сожалению, майкрософтовкий линкер понимает только COFF и Intel OMF, наотрез отказываясь работать с Borland OMF. Delphi же, начиная с третьей версии, перешла с формата Intel OMF на Borland OMF. Поэтому компилировать придется компилятором от Delphi 3.
    Пример минимального HelloWord.
    Code:
    Unit HelloWord;
    Interface
    Procedure Start;
    Implementation
    Function MessageBoxA(hWnd:cardinal; IpText, IpCaption:Pchar; uType:Cardinal): Integer; stdcall; external ‘user32.dll’ name ‘_MessageBoxA@16’;
    Procedure Start;
    Begin
    	MessageBoxA(0,’Hello word!’,nil,0);
    End;
    End.
    
    Ти модуля Unit нужен для того, чтобы компилятор сгенерировал в объектном файле символьные имена объявленных процедур.
    Компилируем:
    Dcc32.exe –JP -$A-,B-,C-,D-,G-,H-,I-,J-,L-,M-,O+,P-,Q-,R-,T-,U-,V-,W+,X+,Y- HelloWord.pas
    Открываем файл HelloWord.obj в HEX-редакторе и смотрим во что превратилась наша точка входа. Допустим Start$wwrv. Теперь выполняем сборку
    Link.exe /ALIGN:32 /FORCE:UNRESOLVED /SUBSYSTEM:WINDOWS /ENTRY:Start$wwrv HelloWord.obj user32.lib /out:Hello.exe
    В результате имеем файл размером 832 байта.
     
    1 person likes this.
  6. W!z@rD

    W!z@rD Борец за русский язык

    Joined:
    12 Feb 2006
    Messages:
    973
    Likes Received:
    290
    Reputations:
    43
    в библиотеках (касается Delphi) если передаешь string данные от лучше заюзать переменную типа ShortString либо добавить ShareMem в раздел Uses... Имхо 1 лучше...
    а если явно передаваемые данные string (например Edit1.Text), преобразуй ShortString(Edit1.Text);
    таким образом можно пребразовывать не только ShortString, но допустим и из string в PChar=> pchar(edit1.text)
     
  7. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Вот нашел несколько полезных советов для M$ Visual Studio.
    Кому интересно - оригинал тут
    Я приведу собственный перевод с моими комментариями.

    1. Учим Visual Studio раскрывать структуры и классы в отладчике
    Редактируем файл \Program Files\Microsoft Visual Studio\Common\MSDev98\Bin. Его формат можно определить методом тыка, посмотрев уже существующие строки. (Там все не так уж и сложно. В начале файла есть немаленький комментарий про синтаксис. Насколько я понял, просто вместо древовидной структуры объекта или структуры будет показываться одно значение. Например, для CString - m_pchData - Прим. Great).

    2. Добавляем слова для подсветки.
    Если создать файл usertype.dat в каталоге \Program Files\Microsoft Visual Studio\Common\MSDev98\Bin со словами по одному на строчку, то эти слова студия будет выделять синим цветом как ключевые. (Я уже добавил туда main и WinMain для удобства - Прим. Great).

    3. Как использовать расширение *.cc для C++
    Нужно произвести следующие изменения в реестре:
    и добавить флаг /Tp к флагам компилятора в настройках проекта

    4. Как закрепить меню.
    В Visual Studio панель меню перемещаемая (об этом говорят две полосы слева). Если это мешает, это можно убрать, выставив галочку "Use screen reader compatible menus" в диалоговом окне Tools -> Options -> закладка Worspace

    5. Напоминания при компиляции
    Если у тебя плохо с памятью :) и тебе сложно запомнить, например, что вот тот кусок кода в финальной версии желательно убрать или что в этом коде содержится некритичная проблема, можно сделать себе напоминание:
    Code:
    #define Stringize( L ) #L
    #define MakeString( M, L ) M(L)
    #define $Line MakeString( Stringize, __LINE__ )
    #define Reminder __FILE__ "(" $Line ") : Reminder: "
    
    использовать:
    #pragma message(Reminder "Fix me!")
    В результате компилятор сгенерирует при компиляции сообщение:
    6. Как сделать вручную точку останова
    Просто поставить код
    Code:
    __asm int 3;
    При нажатии на F5, когда выполнение дойдет до этого места, процессор сгенерирует исключение EXCEPTION_BREAKPOINT, отладчик его поймает и выведет сообщение - User breakpoint called from code at 0x...

    7. Отладочные значения в различных областях памяти программы
    0xCDCDCDCD Память выделена в куче, не инициализирована
    0xDDDDDDDD Память, выделенная в куче, уже освобождена
    0xFDFDFDFD Заполнитель NoMansLand записывается у границ участка памяти для контроля распространенного типа ошибки выхода за границы массива и переполнения. После затирании это указатели при освобождлении этой памяти free/delete выдаст диалоговое окно: DAMAGE: after ТИП block (#номер) at 0xадрес., где ТИП - обычно Normal - тип освобождаемого участка, номер - номер блока.
    Пример неправильного кода:
    Code:
    	char* a = new char[2];
    	strcpy(a, "aaaaaaaaaaaaaaaaaaaaa");
    	delete a;
    
    Во время выполнения delete отладочная сборка программы выдаст окно "DAMAGE: after Normal block (#55) at 0x00430030".
    0xCCCCCCCC Выделено в стеке, не инициализировано

    8. Предопределенные псевдопеременные во время отладки
    В окно Watch можно добавить следующие "переменные":
    @err - последняя ошибка (GetLastError)
    @tib - адрес Thread Information Block потока
    @clk - время выполнения программы (мс)

    9. Просмотр указателей как массивов
    Обычно, если добавить в окно Watch переменную типа char*, она покажется как строка, а не как массив. Чтобы посмотреть отдельные элементы массива (например, чтобы узнать ASCII-код символа из этой строки), надо добавить в Watch выражение переменная,длина, где переменная - переменная указательного типа, длина - за массив какой длины отладчик ее должен считать.
    Например, пусть объявлен char* str;
    Выражение str,10 нам покажет всю строку + отдельно как массив ее первые 10 символов (в массиве - индексы 0-9).
    Выражение ((char*)&main),100 покажет нам первые 100 байт машинного кода функции main

    10. Вызов программных функций во время отладки
    Если у тебя есть функция, например,
    Code:
    int function()
    {
      return 100*2;
    }
    а тебе хочется посмотреть, что бы она возвратила во время отладки, если бы стоял ее вызов в коде - нет проблем! Просто добавляем в окно Watch выражение function() и сразу там появится значение 200

    11. Именование потоков при отладке
    Следущий код содержит функцию, позвляющую назвать поток с указанным ID'ом своим именем
    Code:
    #define MS_VC_EXCEPTION 0x406d1388
    typedef struct tagTHREADNAME_INFO
    {
    	DWORD dwType; // must be 0x1000
    	LPCSTR szName; // pointer to name (in same addr space)
    	DWORD dwThreadID; // thread ID (-1 caller thread)
    	DWORD dwFlags; // reserved for future use, most be zero
    } THREADNAME_INFO;
    void SetThreadName(DWORD dwThreadID, LPCTSTR szThreadName)
    {
    	THREADNAME_INFO info;
    	info.dwType = 0x1000;
    	info.szName = szThreadName;
    	info.dwThreadID = dwThreadID;
    	info.dwFlags = 0;
    	__try 
    	{
    		RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (DWORD *)&info);
    	}
    	__except (EXCEPTION_CONTINUE_EXECUTION)
    	{
    	}
    }
    Имя потока появится в окне Debug->Threads.
     
    #7 _Great_, 8 Dec 2006
    Last edited: 8 Dec 2006
    2 people like this.
  8. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Не совсем в тему, но все же. Поиск базы длл по ее имени в адресном пространстве другого процесса (аналог GetModuleHandle, но для др. адресного пространства).
    Может, кому понадобится. Писал я по просьбе протеуса ну и соотв. там мои норкоманские идеи :)
    Итак, че же для этого я сделал.
    Ищем первый поток процесса. С помощью GetThreadSelectorEntry получаем запись LDT по селектору FS = 0x3b. Открываем Thread Environment Block, из него Process Environment Block, из него - структуру данных загрузчика (все это через ReadProcessMemory), потом проходимся по списку модулей.

    Code:
    	const DWORD FS = 0x3B;
    	DWORD ownerProcessId = 38056;
    
    	// Open process
    	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, ownerProcessId);
    	if(!hProcess)
    		return printf("Cannot open process\n");
    
    	// Find thread
    	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    	THREADENTRY32 te = {sizeof(te)};
    	Thread32First(hSnapshot, &te);
    	DWORD threadId = 0;
    	do
    	{
    		if(te.th32OwnerProcessID == ownerProcessId)
    			threadId = te.th32ThreadID;
    	}
    	while(Thread32Next(hSnapshot, &te));
    	if(!threadId)
    		return printf("No threads for specified process id\n");
    
    	// Get FS base
    	HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, threadId);
    	if(!hThread)
    		return printf("Cannot open thread\n");
    
    	LDT_ENTRY fs_entry;
    	GetThreadSelectorEntry(hThread, FS, &fs_entry);
    
    	DWORD fs_base = fs_entry.BaseLow | (fs_entry.HighWord.Bytes.BaseMid<<16) | (fs_entry.HighWord.Bytes.BaseHi<<24);
    
    	// Read modules list
    	TEB teb;
    	DWORD read;
    	if(!ReadProcessMemory(hProcess, (LPCVOID) fs_base, &teb, sizeof(teb), &read))
    		return printf("Can't read process memory for TEB, %d bytes read\n", read);
    
    	PEB peb;
    	if(!ReadProcessMemory(hProcess, (LPCVOID) teb.Peb, &peb, sizeof(peb), &read))
    		return printf("Can't read process memory for PEB, %d bytes read\n", read);
    
    	PEB_LDR_DATA pld;
    	if(!ReadProcessMemory(hProcess, (LPCVOID) peb.LoaderData, &pld, sizeof(pld), &read))
    		return printf("Can't read process memory for PEB_LDR_DATA, %d bytes read\n", read);
    
    	LDR_MODULE entry;
    	if(!ReadProcessMemory(hProcess, (LPCVOID) pld.InInitializationOrderModuleList.Flink, &entry, sizeof(entry), &read))
    		return printf("Can't read process memory for LDR_MODULE, %d bytes read\n", read);
    
    	WORD wbuffer[20];
        char buffer[1024];
    
        // Walk the list
        do
        {
            // Print the name
    		if(!ReadProcessMemory(hProcess, (LPCVOID) entry.BaseDllName.Buffer, &wbuffer, sizeof(wbuffer), &read))
    			return printf("Can't read process memory for wbuffer in loop, %d bytes read\n", read);
    		
            WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer, sizeof(buffer), 0, 0);
            printf("%s\t[0x%08x]\n", buffer, entry.BaseAddress);
    
            // Next
    		if(!ReadProcessMemory(hProcess, (LPCVOID) entry.ModuleList.Flink, &entry, sizeof(entry), &read))
    			return printf("Can't read process memory for LDR_MODULE in loop, %d bytes read\n", read);
        }
        while(entry.ModuleList.Flink != pld.InInitializationOrderModuleList.Flink);
    
    	CloseHandle(hThread);
    	CloseHandle(hProcess);
    	CloseHandle(hSnapshot);
    
     
    1 person likes this.
  9. Cr4sh

    Cr4sh net maniac

    Joined:
    25 Aug 2005
    Messages:
    30
    Likes Received:
    22
    Reputations:
    27
    уж0с... вот то же самое, но без изврата =/
    Code:
    DWORD RemoteGetModuleHandle(char *name, DWORD dwPid)
    {
    	MODULEENTRY32 m = {sizeof(m)};
             DWORD dwRet = 0;
    
    	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);
    	if (hSnap == INVALID_HANDLE_VALUE) 
    		return NULL; 
    
    	if (!Module32First(hSnap, &m)) 
    		return NULL;
    	do {
    		if (!lstrcmpi(m.szModule, name)) 
                      { 
    			dwRet = m.modBaseAddr;
                               break;
                      }
    	} while (Module32Next(hSnap, &m));
    
    	CloseHandle(hSnap);
    	
    	return dwRet;
    }
    
     
    2 people like this.
  10. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Cr4sh, нуууууу ))) так не прикольно :)) Прикольно вручную анализировать ))
    Хотя изобретать велосипед, конечно, тоже не стоит )
     
  11. Cr4sh

    Cr4sh net maniac

    Joined:
    25 Aug 2005
    Messages:
    30
    Likes Received:
    22
    Reputations:
    27
    зато я больше чем уверен, что этот код будет стабильно работать на всей линейке от NT 4.0 и до Висты ;))
     
  12. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Перенос программы в другое адресное пространство.

    Был замечательный метод по инжекту кода в другую программу - вся наша программа постранично копировалась в чужое АП (адресное пространство) по тем же адресам со всеми заголовками, секциями и прочим.

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

    Предлагаю модификацию: поиск наилучшего достаточного места в другом АП, копирование программы, коррекция фиксапов:

    Code:
    // Get VA
    #define RVATOVA( base, offset )(((DWORD)(base) + (DWORD)(offset))) 
    
    // Move program's memory
    void __CopyMemoryAcrossProcesses( HANDLE hProcess, char* pMemLocal, char* pMemRemote )
    {
    	DWORD dwOldProt, dwNumBytes, i;
    	MEMORY_BASIC_INFORMATION mbi;
    	 
    	VirtualQueryEx(hProcess, pMemRemote, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
    	while (mbi.Protect!=PAGE_NOACCESS && mbi.RegionSize!=0)
    	{
    		if (!(mbi.Protect & PAGE_GUARD))
    		{
    			for (i = 0; i < mbi.RegionSize; i += 0x1000)
    			{
    				VirtualProtectEx(hProcess, pMemRemote + i, 0x1000,PAGE_EXECUTE_READWRITE, &dwOldProt);
    				WriteProcessMemory(hProcess, pMemRemote + i, pMemLocal + i, 0x1000, &dwNumBytes);
    			}
    		}
    		pMemLocal += mbi.RegionSize;
    		pMemRemote += mbi.RegionSize;
    		VirtualQueryEx(hProcess, pMemRemote, &mbi, sizeof(MEMORY_BASIC_INFORMATION));	
    	}
    }
    
    bool TransferProgram(HANDLE hProcess)
    {
    	HMODULE g_module = GetModuleHandle(0);
    	VirtualFreeEx(hProcess, g_module, 0, MEM_RELEASE);
    	
    	DWORD dwSize = ((PIMAGE_OPTIONAL_HEADER)((LPVOID)((BYTE *)(g_module) + ((PIMAGE_DOS_HEADER)(g_module))->e_lfanew + 
    		sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER))))->SizeOfImage;
    
    	char *pMem = (char *)VirtualAllocEx(hProcess, g_module, dwSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    	if(pMem == NULL) return FALSE;
    
    	__CopyMemoryAcrossProcesses( hProcess, (char*) g_module, pMem );
    
    	return true;
    }
    
    DWORD TransferProgramEx(HANDLE hProcess)
    /*
     Return value: image base delta, -1 on error
    */
    {
    	HMODULE hmodule = GetModuleHandle(0);
    	DWORD dwSize = ((PIMAGE_OPTIONAL_HEADER)((LPVOID)((BYTE *)(hmodule) + ((PIMAGE_DOS_HEADER)(hmodule))->e_lfanew + 
    		sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER))))->SizeOfImage;
    
    	MEMORY_BASIC_INFORMATION mbi = {0};
    	VirtualQueryEx( hProcess, hmodule, &mbi, sizeof(mbi) );
    
    	LPVOID Allocated;
    
    	// Memory isn't free, relocate
    	if( mbi.Protect!=PAGE_NOACCESS && mbi.RegionSize!=0 )
    	{
    __try_relocate:
    		LPVOID DesiredAddress = hmodule;
    		*(DWORD*)&DesiredAddress += mbi.RegionSize;
    
    		for(;;)
    		{
    			Allocated = VirtualAllocEx( hProcess, DesiredAddress, dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
    			if( Allocated )
    				break;
    			*(DWORD*)&DesiredAddress += dwSize;
    		}
    	}
    	else
    	{
    		Allocated = VirtualAllocEx( hProcess, hmodule, dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
    		if( !Allocated )
    			goto __try_relocate;
    	}
    
    	// move memory
    	__CopyMemoryAcrossProcesses( hProcess, (char*) hmodule, (char*) Allocated );
    
    	DWORD ImageBaseDelta = (DWORD)Allocated - (DWORD)hmodule;
    
    	// Nonzero imagebase delta
    	if( ImageBaseDelta )
    	{
    		// Apply fixups
    		typedef struct 
    		{
    			WORD	Offset:12;
    			WORD	Type:4;
    		} IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY;
    
    		PIMAGE_OPTIONAL_HEADER poh = 
    			(PIMAGE_OPTIONAL_HEADER)(
    				(DWORD)hmodule
    				+ ((PIMAGE_DOS_HEADER)hmodule)->e_lfanew
    				+ sizeof(IMAGE_NT_SIGNATURE)
    				+ sizeof(IMAGE_FILE_HEADER)
    			);
    
    		// No fixups?
    		if( !poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress )
    		{
    			VirtualFreeEx( hProcess, Allocated, dwSize, MEM_DECOMMIT );
    			VirtualFreeEx( hProcess, Allocated, dwSize, MEM_RELEASE  );
    			return -1;
    		}
    
    		PIMAGE_BASE_RELOCATION Reloc = (PIMAGE_BASE_RELOCATION) RVATOVA(poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress, hmodule);
    		int i = 0;
    
    		// Process fixups
    		for( PIMAGE_FIXUP_ENTRY Fixup = (PIMAGE_FIXUP_ENTRY)( (DWORD)Reloc + sizeof(IMAGE_BASE_RELOCATION) );
    		     (DWORD)Fixup < (DWORD)Reloc + Reloc->SizeOfBlock -2;
    			 Fixup ++, i ++
    				 )
    		{
    			if( Fixup->Type == IMAGE_REL_BASED_HIGHLOW )
    			{
    				DWORD* Patch = (DWORD*)RVATOVA( Reloc->VirtualAddress + Fixup->Offset, Allocated );
    				
    				DWORD t, r;
    				BOOL b = 1;
    
    				// correct fixup
    				b &= ReadProcessMemory( hProcess, Patch, &t, 4, &r );
    				t += ImageBaseDelta;
    				b &= WriteProcessMemory( hProcess, Patch, &t, 4, &r );
    
    				if( !b ) 
    				{
    					// smth wrong
    					VirtualFreeEx( hProcess, Allocated, dwSize, MEM_DECOMMIT );
    					VirtualFreeEx( hProcess, Allocated, dwSize, MEM_RELEASE  );
    					return -1;
    				}
    			}
    			else 
    			{
    				// unsupported fixup type
    				VirtualFreeEx( hProcess, Allocated, dwSize, MEM_DECOMMIT );
    				VirtualFreeEx( hProcess, Allocated, dwSize, MEM_RELEASE  );
    				return -1;
    			}
    		}
    	}
    
    	return ImageBaseDelta;
    }
    TransferProgram - старый вариант
    TransferProgramEx - моя модификация

    Использовать примерно так:

    Code:
    	DWORD ImageBaseDelta = TransferProgramEx( hProcess );
    
    	switch(ImageBaseDelta)
    	{
    	case -1:
    		return printf("Cannot copy body\n"), 0;
    
    	case 0:
    		printf("Program copied at the same addresses\n");
    		break;
    
    	default:
    		printf("Program relocated (delta = 0x%08x)\n", ImageBaseDelta);
    		break;
    	}
    
    	DWORD thID;
    	HANDLE hTh;
    
    	hTh = CreateRemoteThread( hProcess, 0, 0, (LPTHREAD_START_ROUTINE)((char*)RemoteThread+ImageBaseDelta), 0, 0, &thID);
    	WaitForSingleObject( hTh, INFINITE );
    
     
    3 people like this.
  13. gevara

    gevara Elder - Старейшина

    Joined:
    29 Nov 2006
    Messages:
    47
    Likes Received:
    7
    Reputations:
    5
    _Great_
    Вообще для бряка я создаю макрос
    #define __INT3 __asm int 3;
    более наглядней.

    Удобно при написании дров использовать Int 3 причём с опкодом CC, а не с CD 03. тем более что некоторые ядерные отладчики имеют функцию: заменить все int3 (0xCC) на nop (0x90). НО ,как я понимаю, в VC такой возможности нет. обидно.
    ------------------------------------------------------------------------
    Иногда функция должна возвращать результат своей работы (допустим ошибку), который нужно показать юзеру. Удобно делать следующим образом:

    PCHAR func(...)
    {
    ..
    return "ERROR";
    ...
    return "STATUS_SUCCESS";
    }
    ------------------------------------------------------------------------
    Что касается сишных функций - тож можно передавать параметры через регистры. достаточно при объявлении указать __fastcall :

    DWORD __fastcall func1(arg1,arg2,...)
    {
    ...
    }
    ------------------------------------------------------------------------
    Допустим, необходимо проверить 4 символа в строке.

    PCHAR str1;
    ULONG i;
    ...
    if ( (str == 'd') && (str[i+1]== 'c') &&...) ...

    НО лучше так:

    if ( *(PDWORD)&str1 == 'abcd' ) ...

    только нужно учитывать, что компилятор переворачивает символы в строке.
     
  14. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    Способ конвертировать любые типы практически в любые другие.
    К примеру если необходимо загнать в массив значение переменной типа dword, но в том формате в котором она хранится в оперативе (т.е. 4 байта).

    Code:
    type
       massiv=array[0..3] of byte; // тип массива для хранения
       DW2MAS = ^massiv;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
    mas:massiv; // наш массив
    d:dword; // наша Dword переменная
    begin
    d:=$12345678;
    mas:=DW2MAS(@d)^;
    //теперь содержимое массива будет таким: $78,$56,$34,$12
    end;
    
    такимже образом можно окнвертировать и другие типы. Главное чтобы из размер был одинаков. т.е. Dword - некогда не засунуть в array[0..1] of byte;
    Кстати, такимже образом можно и наоборот. засовывать массив из 4-х байт в dword
    Этот метод хорошь тем что работа идет напрямую с память, без использования каких либо дополнительный вычислений.
    -----------------
    При работе с IP адресами многие видели, что они храняться в озу в прямом порядку, т.е. старший байт на старшем месте. т.е. IP - 127.0.0.1 бедет храниться 7F000001
    Это очень не удобно для увелицения значения IP адреса. т.е. к примеру появилась необходимость пройтись по диапазону 192.168.2.1 - 192.168.2.128. Сложность заключается в том что при увеличении значения на 1 будет увеличиваться старшая часть IP адреса, а не младшая (как это нам нужно).
    Для этих целей пожно использовать простой метод основанный на инструкции МП - bswap которая меняет порядок байт.
    Code:
    function incIP(d:dword):dword;assembler;
    asm
    bswap eax // поменять поряд байт
    inc eax // увеличить на 1
    bswap eax // поменять обратно
    end;
    
    -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
    var
    ca:sockaddr_in;
    d:dword;
    begin
    d:=inet_addr('127.0.0.1'); // сохраним IP адресс
    d:=incIP(d); // увеличим на 1
    d:=incIP(d); // увеличим на 1
    d:=incIP(d); // увеличим на 1
    ca.sin_addr.s_addr:=d; // запишем в структуру.
    end;
    
    Тем самым в ca.sin_addr.s_addr будет число которое будет соотвествовать IP - 127.0.0.4
     
  15. sni4ok

    sni4ok Elder - Старейшина

    Joined:
    4 Nov 2006
    Messages:
    115
    Likes Received:
    37
    Reputations:
    12
    более подробно тут
    _http://www.codeguru.com/Cpp/W-P/win32/tutorials/article.php/c9535/
     
  16. gevara

    gevara Elder - Старейшина

    Joined:
    29 Nov 2006
    Messages:
    47
    Likes Received:
    7
    Reputations:
    5
    Ещё не понимаю код типа:
    d=inet_addr('127.0.0.1');
    здесь логичней присваивать значение напрямую: d=0x0100007F;
    хотя, это не так наглядно, но ведь можно
    d=0x0100007F; // 127.0.0.1
    тоже самое с инициализацией портов, юникод строк:
    #define InitUnicodeString(string) {sizeof(string)-2,sizeof(string),string}
     
  17. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    2 gevara Ну это я просто для наглядности юзал функцию винсоковскую.
    Суть была не в том как записать IP адрес, а в том, как увеличить его :)
     
  18. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Вообще-то, __asm int 3 геренит CC, а не CD 03.
    Команда CD 03 отнюдь не аналогична CC и таких "отладочных" свойств, насколько я помню, не имеет.
    CC - Это НЕ просто однобайтовая замена для CD 03.
    Поэтому __asm int 3 генерит сразу CC, а чтобы сгенерить CD 03 надо явно написать
    __asm emit 0xCD
    __asm emit 0x03

    2all - постирал несколько постов, заканчиваем офтопить
     
  19. Joker-jar

    Joker-jar Elder - Старейшина

    Joined:
    11 Mar 2007
    Messages:
    581
    Likes Received:
    205
    Reputations:
    37
    Delphi.

    Функция округления числа до нужного количества знаков:
    Code:
    function RoundFloat(R: Extended; Decimals: Integer): Extended;
    var
      Factor: Extended;
    begin
      Factor := Int(Exp(Decimals * Ln(10)));
      Result := Round(Factor * R) / Factor;
    end;
    Используя вышеописанную функцию, получаем окгугленный размер файла в килобайтах (наиболее универсальная величина для большинства файлов):
    Code:
    Function FileSizeInKb(YourFile : String) : string;
    Var
      arg: extended;
      F : Integer;
    Begin
      F:=FileOpen(YourFile,0);
      arg := FileSeek(F,0,2);
      FileClose(F);
      arg:=roundfloat(arg/1024,1);
      result:=floattostr(arg);
    End;
    Функция проверяет, открыт ли порт на хосте, с указанием времени ожидания ответа. Отличная вещь для написания сканера одного порта на диапазоне адресов. Зависимость - winsock2, обертку под делфню можно найти хде угодно
    Code:
    function PingPort(host: string; portnum: word; tim: integer): boolean;
    var
      k: integer;
      s, opt: Integer;
      FSocket: TSOCKET;
      busy : boolean;
      addr : TSockAddr;
      hEvent : THandle;
      fset : TFDset;
      tv : TTimeval;
      tec :PServEnt;
      PName:String;
      GInitData : TWSADATA;
    begin
      result := false;
      WSAStartup(MAKEWORD(2,0), GInitData);
      addr.sin_family := AF_INET;
      addr.sin_addr.s_addr := INADDR_ANY;
      addr.sin_port := htons(portnum);
      hEvent := WSACreateEvent();
      busy:=false;
      FSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
      WSAEventSelect(FSocket, hEvent, FD_WRITE + FD_CONNECT);
      addr.sin_addr := LookupName(host);
      connect(FSocket, @addr, sizeof(addr));
      if WSAGetLastError()=WSAEINPROGRESS then
        begin
          closesocket (FSocket);
          busy:=true;
        end;
      FD_Zero(fset);
      if busy <> true then
        FD_SET (FSocket, fset);
      tv.tv_sec := tim;
      tv.tv_usec := 0;
      s:=select (1, nil, @fset, nil, @tv);
      if busy then
        exit;
      if FD_ISSET (FSocket, fset) then
        begin
          s:=Sizeof(Opt);
          opt:=1;
          getsockopt(FSocket, SOL_SOCKET, SO_ERROR, @opt, s);
          if opt=0 then
            result := true;
          closesocket(FSocket);
        end;
      WSACloseEvent(hEvent);
      WSACleanup;
    end;
    Шустрый метод копирования файлов. При правильной подборке размера буфера можно добиться космической скорости :)
    Code:
    procedure CopyFile(file1,file2: string);
    var
      FromF, ToF: file;
      NumRead, NumWritten: Integer;
      Buf: array[1..2048] of Char;
    begin
      AssignFile(FromF, file1);
      Reset(FromF, 1);
      AssignFile(ToF, File2);
      Rewrite(ToF, 1);
        repeat
          BlockRead(FromF, Buf, SizeOf(Buf), NumRead);
          BlockWrite(ToF, Buf, NumRead, NumWritten);
        until 
          (NumRead = 0) or (NumWritten <> NumRead);
      CloseFile(FromF);
      CloseFile(ToF);
    end;
     
  20. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    >>Шустрый метод копирования файлов. При правильной подборке размера буфера можно добиться космической скорости

    вообщето апишка есть для этого