![]() |
Кодерские tips and tricks
Кодерские tips’n’tricks
В данной теме делимся программерскими трюками. Это могут быть советы по оптимизации, нестандартные приемы, интересные (но не слишком большие) куски кода. Не стоит превращать тему в свалку исходников, выбирайте наиболее интересные и полезные участки кода. Желательно откомметировать трюк, чтобы всем было понятно. Delphi Оптимизация [Передача аргументов] Никогда не передавай функции в качестве параметра структуру, лучше передавать указатель на нее Код:
//Ни в коем случае не делай так.[Функции - инварианты] Довольно распространенная ошибка программистов – присутствие функций - инвариантов в цикле. Код:
//Неоптимизированный вариантКод:
//Так лучшеКод:
// Неоптимизированный вариантКод:
// Оптимизированный вариантДля уменьшения размера скомпилированных прог исправим модели System.pas и SysInit.pas, удалив все «лишнее». Затем перекомпилируем их и полученные dcu-файлы поместим в папку с прогой. Минимальный System.pas Код:
unit System;Код:
unit SysInit;Код:
Dcc32.exe – Q System.pas SysInit.pas –M –Y –Z -$D- -OНекоторые трюки [Обмен] Обмен значений между двумя переменными без привлечения третьей. Код:
x:=x xor y; |
Оптимизация выходного файла в Microsoft Visual C++.
Параметры линкера для уменьшения размера: - объединение секций Код:
/MERGE:.data=.text /MERGE:.rdata=.textКод:
/ALIGN:32Код:
/ALIGN:512- смена точки входа Код:
/ENTRY:mainКод:
/ENTRY:WinMain- еще можно убрать нафиг CRT или линковать ее динамически: Код:
/NODEFAULTLIB msvcrt.libКод:
#include <windows.h>Дизассемблерный листинг точки входа не содержит ни одного лишнего байта: Код:
00400230 >/$ 55 PUSH EBP/ALIGN:16 /DRIVER это нужно вписать в настройки проекта, в #pragma comment(linker) это не прокатит |
Delphi
Чтобы во время выполнения циклов, не происходил эффект зависания, нужно в теле цикла вставить Код:
Application.ProcessMessages; |
в VCL (delphi) есть "перехват" закрытия формы
OnCloseQuery (где canclose boolean типа который и дает "разрешение" на закрытие формы)... |
Экстремально маленький Hello Word! на Delphi.
Собирать исполняемый файл линкером от Microsoft. К сожалению, майкрософтовкий линкер понимает только COFF и Intel OMF, наотрез отказываясь работать с Borland OMF. Delphi же, начиная с третьей версии, перешла с формата Intel OMF на Borland OMF. Поэтому компилировать придется компилятором от Delphi 3. Пример минимального HelloWord. Код:
Unit HelloWord;Компилируем: 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 байта. |
в библиотеках (касается Delphi) если передаешь string данные от лучше заюзать переменную типа ShortString либо добавить ShareMem в раздел Uses... Имхо 1 лучше...
а если явно передаваемые данные string (например Edit1.Text), преобразуй ShortString(Edit1.Text); таким образом можно пребразовывать не только ShortString, но допустим и из string в PChar=> pchar(edit1.text) |
Вот нашел несколько полезных советов для 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++ Нужно произвести следующие изменения в реестре: Цитата:
4. Как закрепить меню. В Visual Studio панель меню перемещаемая (об этом говорят две полосы слева). Если это мешает, это можно убрать, выставив галочку "Use screen reader compatible menus" в диалоговом окне Tools -> Options -> закладка Worspace 5. Напоминания при компиляции Если у тебя плохо с памятью :) и тебе сложно запомнить, например, что вот тот кусок кода в финальной версии желательно убрать или что в этом коде содержится некритичная проблема, можно сделать себе напоминание: Код:
#define Stringize( L ) #LЦитата:
Просто поставить код Код:
__asm int 3;7. Отладочные значения в различных областях памяти программы 0xCDCDCDCD Память выделена в куче, не инициализирована 0xDDDDDDDD Память, выделенная в куче, уже освобождена 0xFDFDFDFD Заполнитель NoMansLand записывается у границ участка памяти для контроля распространенного типа ошибки выхода за границы массива и переполнения. После затирании это указатели при освобождлении этой памяти free/delete выдаст диалоговое окно: DAMAGE: after ТИП block (#номер) at 0xадрес., где ТИП - обычно Normal - тип освобождаемого участка, номер - номер блока. Пример неправильного кода: Код:
char* a = new char[2];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. Вызов программных функций во время отладки Если у тебя есть функция, например, Код:
int function()11. Именование потоков при отладке Следущий код содержит функцию, позвляющую назвать поток с указанным ID'ом своим именем Код:
#define MS_VC_EXCEPTION 0x406d1388 |
Не совсем в тему, но все же. Поиск базы длл по ее имени в адресном пространстве другого процесса (аналог GetModuleHandle, но для др. адресного пространства).
Может, кому понадобится. Писал я по просьбе протеуса ну и соотв. там мои норкоманские идеи :) Итак, че же для этого я сделал. Ищем первый поток процесса. С помощью GetThreadSelectorEntry получаем запись LDT по селектору FS = 0x3b. Открываем Thread Environment Block, из него Process Environment Block, из него - структуру данных загрузчика (все это через ReadProcessMemory), потом проходимся по списку модулей. Код:
const DWORD FS = 0x3B; |
Цитата:
Код:
DWORD RemoteGetModuleHandle(char *name, DWORD dwPid) |
Cr4sh, нуууууу ))) так не прикольно :)) Прикольно вручную анализировать ))
Хотя изобретать велосипед, конечно, тоже не стоит ) |
Цитата:
|
Перенос программы в другое адресное пространство.
Был замечательный метод по инжекту кода в другую программу - вся наша программа постранично копировалась в чужое АП (адресное пространство) по тем же адресам со всеми заголовками, секциями и прочим. Все было бы хорошо, пока там память свободна. Если там память занята, метод не предусматривал решения в этом случае. Предлагаю модификацию: поиск наилучшего достаточного места в другом АП, копирование программы, коррекция фиксапов: Код:
// Get VATransferProgramEx - моя модификация Использовать примерно так: Код:
DWORD ImageBaseDelta = TransferProgramEx( hProcess ); |
_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[i] == 'd') && (str[i+1]== 'c') &&...) ... НО лучше так: if ( *(PDWORD)&str1[i] == 'abcd' ) ... только нужно учитывать, что компилятор переворачивает символы в строке. |
Способ конвертировать любые типы практически в любые другие.
К примеру если необходимо загнать в массив значение переменной типа dword, но в том формате в котором она хранится в оперативе (т.е. 4 байта). Код:
typeКстати, такимже образом можно и наоборот. засовывать массив из 4-х байт в dword Этот метод хорошь тем что работа идет напрямую с память, без использования каких либо дополнительный вычислений. ----------------- При работе с IP адресами многие видели, что они храняться в озу в прямом порядку, т.е. старший байт на старшем месте. т.е. IP - 127.0.0.1 бедет храниться 7F000001 Это очень не удобно для увелицения значения IP адреса. т.е. к примеру появилась необходимость пройтись по диапазону 192.168.2.1 - 192.168.2.128. Сложность заключается в том что при увеличении значения на 1 будет увеличиваться старшая часть IP адреса, а не младшая (как это нам нужно). Для этих целей пожно использовать простой метод основанный на инструкции МП - bswap которая меняет порядок байт. Код:
function incIP(d:dword):dword;assembler; |
Цитата:
_http://www.codeguru.com/Cpp/W-P/win32/tutorials/article.php/c9535/ |
Ещё не понимаю код типа:
d=inet_addr('127.0.0.1'); здесь логичней присваивать значение напрямую: d=0x0100007F; хотя, это не так наглядно, но ведь можно d=0x0100007F; // 127.0.0.1 тоже самое с инициализацией портов, юникод строк: #define InitUnicodeString(string) {sizeof(string)-2,sizeof(string),string} |
2 gevara Ну это я просто для наглядности юзал функцию винсоковскую.
Суть была не в том как записать IP адрес, а в том, как увеличить его :) |
Цитата:
Команда CD 03 отнюдь не аналогична CC и таких "отладочных" свойств, насколько я помню, не имеет. CC - Это НЕ просто однобайтовая замена для CD 03. Поэтому __asm int 3 генерит сразу CC, а чтобы сгенерить CD 03 надо явно написать __asm emit 0xCD __asm emit 0x03 2all - постирал несколько постов, заканчиваем офтопить |
Delphi.
Функция округления числа до нужного количества знаков: Код:
function RoundFloat(R: Extended; Decimals: Integer): Extended;Код:
Function FileSizeInKb(YourFile : String) : string;Код:
function PingPort(host: string; portnum: word; tim: integer): boolean;Код:
procedure CopyFile(file1,file2: string); |
>>Шустрый метод копирования файлов. При правильной подборке размера буфера можно добиться космической скорости
вообщето апишка есть для этого |
Delphi. Простая, но очень полезная функция (при написании игр, искусственных интеллектов, эмуляторов игровых автоматов и т.д.). Функция идентична random: boolean, только с параметром percent, (0 <= percent <= 100) - вероятностью истинного результата. Например, probrandom(80) вернет true с вероятностью 80%.
Код:
function probrandom(percent: integer): boolean; |
Код:
constЗ.Ы. Рекурсия рулит |
И все-таки, как написано в 1 посте, "Не стоит превращать тему в свалку исходников, выбирайте наиболее интересные и полезные участки кода."
Выбирайте действительно ИНТЕРЕСНЫЙ и НЕОБЫЧНЫЙ код, какието нестандартные приемы там и прочее. Банальщина типа реализации CopyFile не очень нужна. |
Выводит содержимое файла в шестнадцатиричном формате и в виде ASCII кодов.
Код:
#include <iostream> |
штука наверняка не новая, но новичкам может быть пригодится: если возникает трудность с использованием модульности (дополнительных длл-плагинов) в своей программе, а именно в обработке плагином, например, своего объекта (так как по-хорошему мы не знаем как он представляется в памяти и видимо не должны знать по причине того, что плагин может быть написан абсолютно на чем угодно), то можно написать свою дополнительную длл, которая имеет набор функций по созданию\обработке обьекта (да хоть переменной какой-то структуры) в пространстве процесса из которого происходила загрузка сего и ее использовать как плагинной так и основной частью программы.
|
Функция-шаблон считывания из потока n байт.
Написал специально для удобства работы с различными потоками в stl. Так, например, нет стандартного метода считывания n байт из объекта ifstream в string в stl. Код:
template |
Будь-то программирование на winapi, будь-то программирование на posix при создании многопоточного приложения одинаково часто возникает неудобство выраженное в виде невозможности стандартным образом передать своей функции через CreateThread/pthread_create более одного аргумента. Пример двух используемых функций по вин thread.c:
Код:
/*Работа в основной программе: Код:
#include <stdio.h> |
Для Microsoft Visual Studio 7 (она же 2003 .NET):
Если нет желания таскать за собой рантайм в том или ином виде, а при его отключении линкер орёт на неразрешенные ссылки на __chkstk, ___securitycookie и @__security_check_cookie@4, можно обойтись следующей реализацией этого (писал сам :)) Код:
// будем тока выделять место в стеке под переменные |
Stl/c++
Преобразование из string в int с контролем целостности ;)
Код:
bool to_number(char const* beg, int sz, int& num)Код:
std::string value; |
что правильнее при написании malware и почему: сначала полностью писать тулзу, а потом править ее так, чтобы не детектилась ав или брать хелло ворлд и добавлять к нему по одной функции на каждом шаге проверяя, что наша прога не палится?
|
Возникла у меня такая идейка. Функции в Си могут возвращать значение в регистрах eax, edx. Иными словами можно заставить функцию возвратить 2 значения вместо того, чтобы не передавать лишний укказатель указатель. Может быть это не совсем корректно, зато иногда бывает удобно.
насколько я разобрался - здесь можно поступить двумя способами: LARGE_INTEGER function xx() { } void main() { LARGE_INTEGER a; a = xx(); a.HigthPart .. a.LowPart } или непосредственно к регистрам void main() { int a,b; xx(); __asm{mov b,edx} __asm{mov a,eax} } P.S. для более удобново вызова можно попробовать переопределить операторы... |
Генерации кодов Грея. Пишу для тех, кто знает о чем я. Короче, как вы помните, чувак предложил рекурсивный алгоритм и все такое. Я чуть не попутал, когда убедился, что ту же самую последовательность можно сгенерировать практически одной строкой:
Код:
for i := 0 to N-1 doПример использования. Генерация всех подмножеств множества: Код:
{$apptype console}Код:
X or (Y and Z)Код:
{$apptype console} |
Вот только недавно сталкнулся с одной проблемой в сетевом программировании.
Навреное и другие тоже сталкивались с этим. А именно: когда стоит на компе Kaspersky Antivirus ( на 6 версии - 100%) то любой connect будет удачным. В том смысле: при коннекте к любому компу на заведомо закрытый порт - коннект пройдет удачно, в следствии того, что каспер подменяет IP и порт на localhost:1110 - какбы на свой прокси, с целью проверки трафика и именно по этой причине все коннекты являются удачными. Следовательно необходимо заботится о том, чтобы какимнить обрабом проверять естьли коннект реально или нет! |
Цитата:
__asm{mov b,edx} вот это вообще издевателсьво. Остерегайтесь ассемблерных вставок в windows программах пользователського режима - они там ненужны, кроме того такой С-код непереносим. В gcc специально сделали синтаксис АТ&Т, чтобы неповадно было ассемблер использовать :) |
Цитата:
|
2 DWORD
В том то и дело, что по всем параметрам соединение идет какбы нормальное. потмоу что всё подменяется довольно четко. А писать громозкий код для определения сетевой активности своей проги - тоже както не хочется. Если сам разрабатываешь и серверную и клиентскую часть, то выход прост. А если к примеру делается клиент к уже существующему серверу и идет жесткая организация пакетов типа по RFC то тогда возникает проблема. К примеру у меня тесты показали, что даже даже первые данных посланные через send удачно посылаются, даже если сервер недоступен. Просто данные шлются касперу, а от него теряются. Хотя повторная отправка пакета - уже выдает ошибку отправки, при отключеном сервере. |
2slesh очень странно, в принципе, чисто теоретически, подобный перехват со стороны касперского должен официально подделывать возвращаемые ip-пакеты (адрес отправителя) иначе бы возникли проблемы с работой многих сетевых приложений. насчет connect'а: я так понимаю, что касперский должен выполнять функцию шлюза локальной сети, однако если тут этого не происходит, то видимо он вообще работает с пакетами на высоком уровне))
если так, то лучше избавиться от подобного ПО:) при определенных навыках можешь поработать с сырыми пакетами и посмотреть кто-что посылает. по-хорошему касперский должен быть ТОЧНО ip-прокси. |
Касперский не перестаёт нас удивлять. А что же дальше? Инжект во все процессы, с целью посмотреть, что они будут делать? Заражение .ехе файлов на диске с целью закрепить свою защиту в системе?
|
Цитата:
Так что уточни, что ты имел в виду. |
>>Бред. Что куда неперносимо?
>>программах пользователського режима - они там ненужны, кроме того такой С-код непереносим с опущенным словом windows очень даже правильно:) про пользовательский режим - ну в общем-то правда, хотя наверняка найдутся примеры чья оптимизированная реализации на ассемблере будет нужна по части глупости компилятора. а C-код и правда не переносим будет, ведь асм-вставки это не стандарт языка. |
| Время: 01:58 |