· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.WIN32 —› Парсим файл в поисках скан-строки ;-)

. 1 . 2 . 3 . >>

Посл.отвђт Сообщенiе


Дата: Дек 2, 2003 19:22:16

Требуется найти в PE файле строку байт вида:
BE 00 00 00 00 8B 95 00 00 00 00 03 F2 8B 46
где на месте нулей могут быть любые байты,
найти конкретно одну такую строку не проблема, но если
таких строк много, хотелось бы иметь процедуру, которая
бы получала скан-строку и пределы в которых искать, а на
выходе бы выдавала адрес первого байта строки, ну и ноль
понятное дело в случае неудачи.
Кто-нибудь писал такое?, какой может быть алгоритм у такой
процедуры? Любые предложения приветствуются.


Дата: Дек 2, 2003 19:34:13

Asterix
Ну так ищешь BE, прибавляешь к счетчику 5, сравниваешь с 8B 95 и т.д. Потом повторяешь снова. В чем проблема то? Если я чего-то не понял поправь.


Дата: Дек 2, 2003 19:44:11

hello_world

А если последовательность будет другая?

Хранить можно это в виде структуры, которая содержит основную строку, и количество нулей между какими-нибудь символами. а следом массив структур с длиной и смещениями этих нулей, например так:

str db 0BEh, 8Bh, 95h, 03h, 0F2h, 8Bh, 46h

seq struct
str dd ?
null_entries dd ?
seq ends

null_entry struct
offset dd ?
len dd ?
null_entry ends


короче в памяти всё это дело будет выглядеть так:

seq1 dd offset str, 2 ;структура seq
dd 1, 4 ;первая структура null_entry
dd 3, 4 ;вторая структура


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


Дата: Дек 2, 2003 20:13:19

Asterix

Я тебе самое классное решение предложу. Такое как в PE Sniffer, если NEOx когда-нибудь разродится переписать по моему примеру. Называется - регулярные выражения! Лови:
#include <windows.h>
#include <stdio.h>
#include "pcre.h" /*сгружай портированную версию с pcre.org*/

/*пример написан по мотивам pcredemo.c*/

#define OVECCOUNT 90    /* согласно требованиям автора должно делиться на 3 */
/*
1. Загрузить все функции
2. Спроецировать файл
3. Скомпилировать регулярное выражение
4. По принципу FindFileFirst/FindFileNext достать его.
*/
int main(void)
{
	HMODULE hpcre				= 0;
	HANDLE hfile = 0, hmapfile	= 0;
	LPVOID lpvMem				= 0;

	/*PCRE stuff*/
	pcre	*re			= 0;
	char pattern[]		= "\x8B\x1E\x83\xEE\xFC";	//UPX - именно шестнадцатеричные!
	const char *error	= 0;
	int erroffset		= 0;
	int ovector[OVECCOUNT];
	int subject_length	= 0;
	int rc				= 0;

	/*pointers to PCRE functions*/
    pcre *(*pcre_compile)(const char *pattern, int options,
            const char **errptr, int *erroffset,
            const unsigned char *tableptr);  
	int (*pcre_exec) (const pcre *code, const pcre_extra *extra,
            const char *subject, int length, int startoffset,
            int options, int *ovector, int ovecsize);

	/******************************************************************** /
	if(!(hpcre = LoadLibraryA("pcre.dll")))
		return -1;
	if(!(*(DWORD *)&pcre_compile = (DWORD)GetProcAddress(hpcre, "pcre_compile")))
		return -2;
	if(!(*(DWORD *)&pcre_exec = (DWORD) GetProcAddress(hpcre, "pcre_exec")))
		return -3;

	hfile = CreateFile("calc.upx.exe", GENERIC_ALL, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
	if (hfile == INVALID_HANDLE_VALUE)
		return -4;
	
	subject_length = (int) GetFileSize (hfile, 0);	
	hmapfile = CreateFileMapping(hfile, 0, PAGE_READWRITE, 0, 0, 0);
 	lpvMem = MapViewOfFile(hmapfile, FILE_MAP_ALL_ACCESS, 0, 0, 0);

	/*не с lpvMem, а с секций! - незачем прогонять через заголовок! - поправь*/

	re = pcre_compile(
		pattern,              /* последовательность для поиска - бинарные байты - БЕЗ ДВОИЧНЫХ НУЛЕЙ!!!! */
		/*двоичные нули ты заранее можешь заменять регулярным выражением типа один символ один раз*/
		0,                    /* default options */
		&error,               /* for error message */
		&erroffset,           /* for error offset */
		NULL);                /* use default character tables */

	/* Compilation failed: print the error message and exit */
	if (!re)
	{
		printf("PCRE compilation failed at offset %d: %s\n", erroffset, error);
		return -5;
	}	
 
	/*это найдет сигнатуру ТОЛЬКО РАЗ! Все остальные разы будем искать ниже*/
	rc = pcre_exec(
		/*заметь, файл УЖЕ МОЖЕТ содержать двоичные нули! - см. telock.exe - он обрабатывается корректно*/
		re,                   /* скомпилированное выражение из  pattern*/
		NULL,                 /* no extra data - we didn't study the pattern */
		(char*) lpvMem,       /* the subject string - lpvMem from CreateFileMapping*/
		subject_length,       /* the length of the subject  - from GetFileSize*/
		0,                    /* start at offset 0 in the subject */
		0,                    /* default options */
		ovector,              /* output vector for substring information */
		OVECCOUNT);           /* number of elements in the output vector */

	CloseHandle(hfile);
	CloseHandle(hmapfile);

	/* Matching failed: handle error cases */
	if (rc < 0)
	{
		switch(rc)
		{
			case PCRE_ERROR_NOMATCH: printf("No match\n"); 
				break;
			default: printf("Matching error %d\n", rc); 
				break;
		}
		UnmapViewOfFile(lpvMem);
		return -6;
	}

	/* Match succeded */
	printf("\nMatch succeeded at offset %d\n", ovector[0]); //--> test for PE section, etc...

	/*теперь пошли искать все остальные вхождения*/
	for (;;)
	{
		int options = 0;                 /* Normally no options */
		int start_offset = ovector[1];   /* Start at end of previous match */

		/* If the previous match was for an empty string, we are finished if we are
		at the end of the subject. Otherwise, arrange to run another match at the
		same point to see if a non-empty match can be found. */

		if (ovector[0] == ovector[1])
		{
			if (ovector[0] == subject_length) break;
			options = PCRE_NOTEMPTY | PCRE_ANCHORED;
		}

		/* Run the next matching operation */

		rc = pcre_exec(
			re,                   /* the compiled pattern */
			NULL,                 /* no extra data - we didn't study the pattern */
			(char*) lpvMem,       /* the subject string */
			subject_length,       /* the length of the subject */
			start_offset,         /* starting offset in the subject */
			0,						/* options */
			ovector,              /* output vector for substring information */
			OVECCOUNT);           /* number of elements in the output vector */

		if (rc == PCRE_ERROR_NOMATCH)	/*в данном конкретном случае - это не ошибка - см. pcredemo.c*/
		{
			if (options == 0) break;
			ovector[1] = start_offset + 1;
			continue;
		}

		/* однако, если в rc НЕ -1 - значит, ошибка серьезная*/
		if (rc < 0)
		{
			printf("Matching error %d\n", rc);
			UnmapViewOfFile(lpvMem);
			return -7;
		}

		/* Match succeded */
		printf("\nMatch succeeded again at offset %d\n", ovector[0]);

		/* The match succeeded, but the output vector wasn't big enough. */
		if (rc == 0)
		{
			rc = OVECCOUNT/3;
			printf("ovector only has room for %d captured substrings\n", rc - 1);
		}
	}

	UnmapViewOfFile(lpvMem);
	return -8;
}


Дата: Дек 2, 2003 20:14:28

hello_world

Ну ты конечно не понял :-) Я знаю как искать скан-строку
в файле, но
мне хотелось иметь более универсальный вариант, помоему я в своём посте
вполне понятно об этом написал.

dragon

Это мне нужно обдумать ;-). Спасибо.


Дата: Дек 2, 2003 20:15:47

Ух, volodya!, жалко только что на C, будем разбираться ;-)


Дата: Дек 2, 2003 20:45:14

Сделаю поправочку, файл у меня уже в памяти, стоит
под отладчиком(моим), доступ к нему имею через ReadProcessMemory,
поэтому маппить ничего не требуется.


Дата: Дек 2, 2003 20:54:08

Asterix

Если что-то неясно - спрашивай. Синтаксис твоего HLL достаточно близок, поэтому %80 понять ты сможешь. Если чего-то не догонишь, тогда спрашивай.


Дата: Дек 3, 2003 01:08:12 · Поправил: hello_world

Asterix
Исправляюсь :)
Вот функцию написал:
.data


nesovp db "не "
sovp db "совпали!",0


scan    db    0aah,0,0,0,0bbh,0,0cch

file    db    1,2,3,4,0aah,5,6,7,0bbh,8,0cch,9,10,11,12,13,14,15

.code

start:


push sizeof scan
push offset scan
push offset file
push sizeof file
call ScanSearch


.if eax==0
invoke MessageBox,0,offset sovp,offset sovp,0
.else
invoke MessageBox,0,offset nesovp,offset nesovp,0
.endif

invoke ExitProcess,0

;#########################################################
ScanSearch: ; длина 51 байт !!! :)

;Usage:
;
;push размер сканстроки
;push offset сканстроки
;push offset файла
;push размер файла
;call ScanSearch
;
;Return:
;
;eax == 0 - found
;edi - offset

dec dword ptr [esp + 8]
mov eax,[esp + 16]
add esp,4
pop edx
sub edx,eax
inc edx
inc edx

___search:

dec edx
test edx,edx
jz ___exit


pop edi
pop esi
pop ecx

inc edi

push ecx
push esi
push edi


___lodsb:

lodsb
test al,al
jnz ___cmpsb

inc edi
loop ___lodsb
jcxz ___exit


___cmpsb:

dec esi
cmpsb
jnz ___search

loop ___lodsb

___exit:

mov eax,ecx
sub esp,8
ret

;#########################################################

end start

ЗЫ Я знаю что это извращение, но можно уложиться меньше чем в 51 байт.
Если хочешь пиши, а у меня уже бошка болит :)


Дата: Дек 3, 2003 08:24:14

hello_world

Что-то уж больно коротко ;-), ладно я потом подумаю над всем этим и
напишу, у меня в голове тоже крутится идея только пока схватить её не могу ;-)


Дата: Дек 3, 2003 13:06:34

Хе, а я тут вот че придумал...
Можно сделать что-то типа компилятора кода для проверки скан-строки и генерить этот год в рантайме.
Для BE 00 00 00 00 8B 95 00 00 00 00 03 F2 8B 46 скомпилим типа:
     // Вход: esi - указатель на память
     // Выход: eax=1 - скан-строка совпадает, 0 - не совпадает
     xor eax,eax
     cmp byte ptr [esi],0BEh
     jne @exit
     add esi,5
     cmp byte ptr [esi],08Bh
     jne @exit
     inc esi
     ...
     inc eax
   @exit:
     ret


З.Ы. можно обойтись без переходов за счет CMOVcc, но тогда придется проверять всю скан-строку целиком. Может быстрее получится, а может медленнее - проверять надо...


Дата: Дек 3, 2003 13:11:08

...а можно этот компилятор еще немного и прооптимизировать, типа для 2-х подряд идущих байтов использовать cmp word ptr [esi],xxxx, а для четырех cmp dword ptr [esi],xxxx... а для 16-и подключить SSE2 регистры... :))


Дата: Дек 3, 2003 18:44:52

Max

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


Дата: Дек 3, 2003 18:55:12

Я предложил универсальное решение. Скорость, правда, низковата...


Дата: Дек 3, 2003 19:06:33

volodya

Я так понимаю твой пример использует pcre.dll, если так то это плохо.

. 1 . 2 . 3 . >>


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.061