Сегодня посмотрел очередную свежую серию MLP:FiM и понял, что мне нечем себя занять. В связи с этим решил включить трек моей любимой группы (как вы, наверное, догадались - это Ранетки :D) и написать что-нибудь эдакое на ассемблере.
По совету друзей, которые маются всякой фигней, вместо того, чтобы заняться чем-нибудь полезным и написать нормальную статью в блог, выбор пал на написание нескольких макросов, которые позволяют вызывать библиотечные функции без использования таблицы импорта.
Минусы: макросы базозависимые.
Плюсы: макросы потокобезопасные.
Anyway: на базе этих макросов можно достаточно быстро построить новые, удобные вам.
Пример использования получившихся макросов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
.486 .model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\macros\macros.asm include \masm32\macros\no_import.asm .code start PROC LOCAL buf[128]:BYTE LOCAL sz:DWORD mov sz, 127 ;подготовка возможности вызовов без таблицы импорта noimport_call_prepare noimport_invoke_load chr$("GetUserNameA"), chr$("advapi32.dll"), addr buf, addr sz ;вызов MessageBoxA с предварительной загрузкой библиотеки user32.dll noimport_invoke_load chr$("MessageBoxA"), chr$("user32.dll"), 0, addr buf, chr$("ЙАААЗЬ"), 0 ;kernel32.dll всегда в памяти, ее не надо предварительно грузить noimport_invoke chr$("ExitProcess"), chr$("kernel32.dll"), 0 ret start ENDP end start |
Сами макросы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
UNICODE_STRING STRUCT xLength dw ? MaximumLength dw ? Buffer dd ? UNICODE_STRING ENDS PEB_LDR_DATA STRUCT 4 xLength dd ? Initialized db ? SsHandle dd ? InLoadOrderModuleList LIST_ENTRY <> InMemoryOrderModuleList LIST_ENTRY <> InInitializationOrderModuleList LIST_ENTRY <> PEB_LDR_DATA ENDS PEB STRUCT InheritedAddressSpace db ? ReadImageFileExecOptions db ? BeingDebugged db ? Spare db ? Mutant dd ? ImageBaseAddress dd ? LoaderData dd ? ProcessParameters dd ? SubSystemData dd ? ProcessHeap dd ? FastPebLock dd ? FastPebLockRoutine dd ? FastPebUnlockRoutine dd ? EnvironmentUpdateCount dd ? KernelCallbackTable dd ? EventLogSection dd ? EventLog dd ? FreeList dd ? TlsExpansionCounter dd ? TlsBitmap dd ? TlsBitmapBits dd 2 dup(?) ReadOnlySharedMemoryBase dd ? ReadOnlySharedMemoryHeap dd ? ReadOnlyStaticServerData dd ? AnsiCodePageData dd ? OemCodePageData dd ? UnicodeCaseTableData dd ? NumberOfProcessors dd ? NtGlobalFlag dd ? Spare2 db 4 dup(?) CriticalSectionTimeout dq ? HeapSegmentCommit dd ? HeapDeCommitTotalFreeThreshold dd ? HeapDeCommitFreeBlockThreshold dd ? NumberOfHeaps dd ? MaximumNumberOfHeaps dd ? ProcessHeaps dd ? GdiSharedHandleTable dd ? ProcessStarterHelper dd ? GdiDCAttributeList dd ? LoaderLock dd ? OSMajorVersion dd ? OSMinorVersion dd ? OSBuildNumber dd ? OSPlatformId dd ? ImageSubSystem dd ? ImageSubSystemMajorVersion dd ? ImageSubSystemMinorVersion dd ? GdiHandleBuffer dd 22h dup(?) PostProcessInitRoutine dd ? TlsExpansionBitmap dd ? TlsExpansionBitmapBits db 80h dup(?) SessionId dd ? PEB ENDS LDR_MODULE STRUCT InLoadOrderModuleList LIST_ENTRY <> InMemoryOrderModuleList LIST_ENTRY <> InInitializationOrderModuleList LIST_ENTRY <> BaseAddress dd ? EntryPoint dd ? SizeOfImage dd ? FullDllName UNICODE_STRING <> BaseDllName UNICODE_STRING <> Flags dd ? LoadCount dw ? TlsIndex dw ? HashTableEntry LIST_ENTRY <> TimeDateStamp dd ? LDR_MODULE ENDS xinvoke MACRO name:REQ, params:VARARG count = 0 FOR xparam, <params> count = count + 1 @CatStr(var,%count) TEXTEQU @CatStr(&xparam) ENDM REPEAT count IF @SizeStr(%@CatStr(var,%count)) GT 4 IFIDNI @SubStr(%@CatStr(var,%count), 1, 4),<addr> __temp_text TEXTEQU @SubStr(%@CatStr(var,%count), 5) lea eax, __temp_text push eax ELSE push @CatStr(var,%count) ENDIF ELSE push @CatStr(var,%count) ENDIF count = count - 1 ENDM mov eax, name call eax ENDM noimport_invoke_load MACRO funcname:REQ, libname:REQ, params:VARARG count = 0 _addr_found = 0 _eax_found = 0 FOR xparam, <params> IFIDNI <&xparam>,<eax> _eax_found = 1 ENDIF count = count + 1 @CatStr(var,%count) TEXTEQU @CatStr(&xparam) ENDM REPEAT count IF @SizeStr(%@CatStr(var,%count)) GT 4 IFIDNI @SubStr(%@CatStr(var,%count), 1, 4),<addr> __temp_text TEXTEQU @SubStr(%@CatStr(var,%count), 5) lea eax, __temp_text push eax _addr_found = 1 ELSE push @CatStr(var,%count) ENDIF ELSE push @CatStr(var,%count) ENDIF count = count - 1 ENDM IF _addr_found EQ 1 IF _eax_found EQ 1 %ECHO [WARNING: EAX is used with ADDR at Line @CatStr(%@Line)] ENDIF ENDIF xinvoke _LoadLibraryAFunc, libname xinvoke _GetProcAddressFunc, eax, funcname call eax ENDM noimport_invoke MACRO funcname:REQ, libname:REQ, params:VARARG count = 0 _addr_found = 0 _eax_found = 0 FOR xparam, <params> IFIDNI <&xparam>,<eax> _eax_found = 1 ENDIF count = count + 1 @CatStr(var,%count) TEXTEQU @CatStr(&xparam) ENDM REPEAT count IF @SizeStr(%@CatStr(var,%count)) GT 4 IFIDNI @SubStr(%@CatStr(var,%count), 1, 4),<addr> __temp_text TEXTEQU @SubStr(%@CatStr(var,%count), 5) lea eax, __temp_text push eax _addr_found = 1 ELSE push @CatStr(var,%count) ENDIF ELSE push @CatStr(var,%count) ENDIF count = count - 1 ENDM IF _addr_found EQ 1 IF _eax_found EQ 1 %ECHO [WARNING: EAX is used with ADDR at Line @CatStr(%@Line)] ENDIF ENDIF xinvoke _GetModuleHandleA, libname xinvoke _GetProcAddressFunc, eax, funcname call eax ENDM noimport_call_prepare MACRO .data? _GetProcAddressFunc dd ? _LoadLibraryAFunc dd ? _GetModuleHandleA dd ? _krnl dd ? .code assume fs:nothing mov eax, fs:[30h] mov eax, PEB.LoaderData[eax] mov eax, PEB_LDR_DATA.InLoadOrderModuleList[eax] mov eax, LDR_MODULE.InLoadOrderModuleList[eax] mov eax, LDR_MODULE.InLoadOrderModuleList[eax] mov eax, LDR_MODULE.BaseAddress[eax] mov edx, eax mov _krnl, eax add eax, IMAGE_DOS_HEADER.e_lfanew mov eax, [eax] add eax, edx add eax, IMAGE_NT_HEADERS.OptionalHeader.DataDirectory.VirtualAddress mov eax, [eax] add eax, edx mov edi, eax add eax, IMAGE_EXPORT_DIRECTORY.AddressOfNames mov eax, [eax] add eax, edx xor esi, esi __find: push eax mov eax, [eax] add eax, edx mov ebx, chr$("GetProcAddress") __compare: mov cl, [eax] mov ch, [ebx] test cl, cl jz __test_ch test ch, ch jz __next jmp __check __test_ch: test ch, ch jz __found jmp __next __check: cmp cl,ch jne __next inc eax inc ebx jmp __compare __next: inc esi pop eax add eax,4 jmp __find __found: pop eax mov eax, edi add eax, IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals mov eax, [eax] add eax, edx add eax, esi add eax, esi mov bx, [eax] movzx ebx, bx mov eax, edi add eax, IMAGE_EXPORT_DIRECTORY.AddressOfFunctions mov eax, [eax] add eax, edx add ebx, ebx add eax, ebx add eax, ebx mov eax, [eax] add eax, edx mov _GetProcAddressFunc, eax xinvoke _GetProcAddressFunc, _krnl, chr$("LoadLibraryA") mov _LoadLibraryAFunc, eax xinvoke _GetProcAddressFunc, _krnl, chr$("GetModuleHandleA") mov _GetModuleHandleA, eax assume fs:error ENDM |
Надеюсь, кому-нибудь пригодится.
Исходные коды одним архивом: скачать.
Обновлено: 14.11.11
Спасибо, весьма интересно! Единственное, что значит базозависимые?
"Минусы: макросы базозависимые."
Значит, что они используют абсолютные смещения (секция данных используется). Вот если бы все макросы только стек и относительные смещения использовали, тогда базозависимыми они бы не были.
А если один из параметров макроса "xinvoke" будет "addr localvar" адресом локальной переменной, то такой макрос должен не сработать. Как быть?
Явно использовать инструкцию lea.
Это само собой, вопрос, непонятно как отличить локальную от глобальной, ведь нужно будет во время генераии push сделать lea reg, push reg.
Что значит "как отличить", если ты сам код пишешь и видишь, где локальные, где глобальные :)
Я предлагаю в макросы не толкать addr, а перед вызовом макроса делать lea, а в макрос уже регистр передавать.
Хотя если немного поколупать макросы, возможно, получится и универсально сделать.
Понял ход твоих мыслей. Это обходной путь, так неинтересно. :) А я думаю как можно сделать полную эмуляцию invoke, чтобы и с адресами локальных переменных не было проблем. Неисключена и такая ситуация, что количество параметров которые должны содержать адреса локальных переменных будет больше, чем рабочих регистров. Можно конечно использовать указатели, но это обходной путь.
Кстати, если в invoke ты используешь и регистр eax, и addr что_то, он ругнется. А чтобы эти макросы заставить работать как invoke, надо над ними поработать еще)
Только что сделал полную эмуляцию invoke, даже предупреждение выдает, если попытаться использовать в одном вызове оператор addr и регистр eax. Завтра выложу.
Обновление добавлено.
Спасибо за обновление, полезный макрос. Предлагаю использовать предупреждение только в том случае, если регистр eax используется левее, чем addr, т.к. только в таком случае регистр eax будет реинициализироваться. Такую проверку можно сделать сохранением индекса параметров, а перед варнингом сделать проверку IF index_eax LT index_addr.
Правильно ли я понимаю, различия между адресом глобальной и локальной переменной нету? Будут идеи как отличить адрес глобальной от локальной переменной?
Адрес никак не отличишь. Разве что, существуют какие-то виндовые функции, позволяющие отличить адрес стека от адреса кучи. Локальные переменные всегда в стеке лежат, глобальные - в куче. Может быть, такое и вручную реализуемо, не задумывался. По идее, где-то должны лежать указатели на начальные адреса стека и кучи процесса...
DX, ты бы не мог сделать не базозависимую версию (для крипторов/пакеров/протекторов)? Я пишу криптор.
Расскажи-ка, что там базозависимого сейчас?
DX:
"Значит, что они используют абсолютные смещения (секция данных используется). Вот если бы все макросы только стек и относительные смещения использовали, тогда базозависимыми они бы не были."
Ну так возьми и напиши базонезависимые макросы, what's the problem? Человек, пишущий криптор сам, должен в таком разбираться.
Здесь можно взять более удобную реализацию такой идеи!
http://www.wasm.ru/srclist.php?list=9#358
А можнобрать функции из адрессного пространства библиотеки без подгрузки самой библиотеки
ГРЕШНОВАТО
Верно толкуешь, Каимий! Какие ж адресные пространства, когда модуль не подгружен?