Jump to content
Tuts 4 You

Memory scan problem


justdream

Recommended Posts

Hi guys,

I've coded a basic memory scanner(DLL) in Delphi to search a string in game's memory.. but I'm with a problem..

After a few seconds scanning, the scan stops.. and I don't know why...

Can you help-me?


function HexToInt(s : string) : Int64;
begin
if (s <> '') and (s[1] <> '$') then
result := strToInt64('$' + s )
else
result := strToInt64(s);
end;procedure FirstScan(valor: string);
var
dwEndAddr : dword;
i:dword;
mbi : TMemoryBasicInformation;
valor:string;
begin
while (VirtualQuery(Pointer(DWORD(mbi.BaseAddress) + MBI.RegionSize), MBI, SizeOf(MEMORY_BASIC_INFORMATION))=SizeOf(TMemoryBasicInformation)) do begin
if (MBI.State = MEM_COMMIT) and (MBI.Protect = PAGE_EXECUTE_READWRITE) and (MBI.RegionSize>0) then begin
dwEndAddr := DWORD(mbi.BaseAddress) + MBI.RegionSize;
for i := DWORD(MBI.BaseAddress) to (dwEndAddr - 1 - sizeof(DWORD)) do begin
Application.ProcessMessages;
try
valor := string(Pchar(i));
if valor = Form1.Edit1.Text then Form1.lbFirstScanFound.Items.Add(IntToHex(i,8));
except
Break;
end;
end;
end;
end;
end;
Edited by justdream
Link to comment

I'd assume it's probably hitting the exception and stopping. Since you didn't say it throws any errors or crashes, there isn't much we can help with. You'd need to debug the DLL and see if its throwing an exception when you are scanning, or if the call to VirtualQuery is failing.

The other thing would be, you are calling VirtualQuery passing the address as:

Pointer(DWORD(mbi.BaseAddress) + MBI.RegionSize)

But you never initialize mbi other then defining it. So you are setting it to 0 or even invalid data depending on how Delphi initializes variables on declaration. Try using a pointer initialized to nil instead as the first param instead. Or just pass nil if Delphi will allow it.

See this example for what I mean:
/>http://forum.madshi.net/viewtopic.php?p=11102&sid=42f78414c2afab26c339704275940ae9#p11102

I'm not a Delphi coder so I can't say that your issue is caused by any coding errors, sorry.

Link to comment

atom0s is correct, the VirtualQuery Api is being passed fields from the MBI structure, which is not initialized until return from the VirtualQuery Api.

Also converting every byte to a possible string to check is gonna be slow, maybe check first char first, then check rest of string.

As you're using VCL you might want to Process form messages in that loop too.

Have fun!

BoB

Link to comment
  • 2 years later...

Hi guys,

so I have a little question also about using the VirtualQuery API.So I have any address xy which does belong to any section of a loaded target file and now after using this API I don't get the top of the section where this address does belong.

Exsample:

; Section Start at: 004B7000 VA | 001AE000 Size; -----------------------------------------------lea edi,[esp-100]push 1Cpush edipush 0060E600   ; <-- exsample address belongs to section abovecall VirtualQueryNow the result in buffer are:$ ==>    0012FEC8  0060C000  Project1.0060C000$+4      0012FECC  00400000  ASCII "MZP"$+8      0012FED0  00000080$+C      0012FED4  00003000$+10     0012FED8  00001000$+14     0012FEDC  00000040$+18     0012FEE0  01000000$+1C     0012FEE4  00000000
So why I don't get the section top of 004B7000 now in buffer?Or is there any other API which I can use to get the infos quickly [right section top and size]?

Thanks

Link to comment
1) Probably memory protection option or page state has changed for that part of the section (eg. somebody called VirtualProtect on the addresses you're interested into).

Quick check - what does Olly memory window show - one big region for a section, or several smaller ones? What does VirtualQuery return for addresses 60BFFF and 60F000? 

 

2) I'd try dbghelp.dll functions, like ImageRvaToSection (http://msdn.microsoft.com/en-us/library/windows/desktop/ms680217(v=vs.85).aspx)

Link to comment

Hi kao,

hmmm,so I did just load the file in Olly and then I tried to use the VQ API with the exsample address so there was nothing changed before etc.So the section are shown normaly = each target section normaly no big one section etc.

0060BFFF | Buffer = 0060B000 | 5000 size0060F000 | Buffer = 0060F000 | 1000 size
Strange or?So before I also thought that you get the right section values of each section which you want to test and now I see it does not work correctly etc.I also tried to alloc some simple free sections [00CE0000 | 01002000 size] and have test the address 19E7AB0 and there I also get 019E7000 back as base address of region!?!

So I think I need to use the PE Header again in this case to find the section top if this API does not work as I thought before.

greetz

Link to comment

Hi


 


Here is a good example from TEDSON


Or google for "c++ memory scanner"



include <windows.h>
#include <stdio.h> #define IS_IN_SEARCH(mb,offset) (mb->searchmask[(offset)/8] & (1<<((offset)%8)))
#define REMOVE_FROM_SEARCH(mb,offset) mb->searchmask[(offset)/8] &= ~(1<<((offset)%8));
double finaladdress;
typedef struct _MEMBLOCK
{
HANDLE hProc;
unsigned char *addr;
int size;
unsigned char *buffer; unsigned char *searchmask;
int matches;
int data_size; struct _MEMBLOCK *next;
} MEMBLOCK; typedef enum
{
COND_UNCONDITIONAL,
COND_EQUALS, COND_INCREASED,
COND_DECREASED,
} SEARCH_CONDITION;
MEMBLOCK* create_memblock (HANDLE hProc, MEMORY_BASIC_INFORMATION *meminfo, int data_size)
{
MEMBLOCK *mb = malloc (sizeof(MEMBLOCK)); if (mb)
{
mb->hProc = hProc;
mb->addr = meminfo->BaseAddress;
mb->size = meminfo->RegionSize;
mb->buffer = malloc (meminfo->RegionSize);
mb->searchmask = malloc (meminfo->RegionSize/8);
memset (mb->searchmask, 0xff, meminfo->RegionSize/8);
mb->matches = meminfo->RegionSize;
mb->data_size = data_size;
mb->next = NULL;
} return mb;
} void free_memblock (MEMBLOCK *mb)
{
if (mb)
{
if (mb->buffer)
{
free (mb->buffer);
} if (mb->searchmask)
{
free (mb->searchmask);
} free (mb);
}
}
void update_memblock (MEMBLOCK *mb, SEARCH_CONDITION condition, unsigned int val)
{
static unsigned char tempbuf[128*1024];
unsigned int bytes_left;
unsigned int total_read;
unsigned int bytes_to_read;
unsigned int bytes_read; if (mb->matches > 0)
{
bytes_left = mb->size;
total_read = 0;
mb->matches = 0; while (bytes_left)
{
bytes_to_read = (bytes_left > sizeof(tempbuf)) ? sizeof(tempbuf) : bytes_left;
ReadProcessMemory (mb->hProc, mb->addr + total_read, tempbuf, bytes_to_read, (DWORD*)&bytes_read);
if (bytes_read != bytes_to_read) break; if (condition == COND_UNCONDITIONAL)
{
memset (mb->searchmask + (total_read/8), 0xff, bytes_read/8);
mb->matches += bytes_read;
}
else
{
unsigned int offset; for (offset = 0; offset < bytes_read; offset += mb->data_size)
{
if (IS_IN_SEARCH(mb,(total_read+offset)))
{
BOOL is_match = FALSE;
unsigned int temp_val;
unsigned int prev_val = 0; switch (mb->data_size)
{
case 1:
temp_val = tempbuf[offset];
prev_val = *((unsigned char*)&mb->buffer[total_read+offset]);
break;
case 2:
temp_val = *((unsigned short*)&tempbuf[offset]);
prev_val = *((unsigned short*)&mb->buffer[total_read+offset]);
break;
case 4:
default:
temp_val = *((unsigned int*)&tempbuf[offset]);
prev_val = *((unsigned int*)&mb->buffer[total_read+offset]);
break;
} switch (condition)
{
case COND_EQUALS:
is_match = (temp_val == val);
break;
case COND_INCREASED:
is_match = (temp_val > prev_val);
break;
case COND_DECREASED:
is_match = (temp_val < prev_val);
break;
default:
break;
} if (is_match)
{
mb->matches++;
}
else
{
REMOVE_FROM_SEARCH(mb,(total_read+offset));
}
}
}
} memcpy (mb->buffer + total_read, tempbuf, bytes_read); bytes_left -= bytes_read;
total_read += bytes_read;
} mb->size = total_read;
}
}
MEMBLOCK* create_scan (unsigned int pid, int data_size)
{
MEMBLOCK *mb_list = NULL;
MEMORY_BASIC_INFORMATION meminfo;
unsigned char *addr = 0; HANDLE hProc = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); if (hProc)
{
while (1)
{
if (VirtualQueryEx (hProc, addr, &meminfo, sizeof(meminfo)) == 0)
{
break;
}
#define WRITABLE (PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)
if ((meminfo.State & MEM_COMMIT) && (meminfo.Protect & WRITABLE))
{
MEMBLOCK *mb = create_memblock (hProc, &meminfo, data_size);
if (mb)
{
mb->next = mb_list;
mb_list = mb;
}
}
addr = (unsigned char*)meminfo.BaseAddress + meminfo.RegionSize;
}
} return mb_list;
}
void free_scan (MEMBLOCK *mb_list)
{
CloseHandle (mb_list->hProc); while (mb_list)
{
MEMBLOCK *mb = mb_list;
mb_list = mb_list->next;
free_memblock (mb);
}
} void update_scan (MEMBLOCK *mb_list, SEARCH_CONDITION condition, unsigned int val)
{
MEMBLOCK *mb = mb_list;
while (mb)
{
update_memblock (mb, condition, val);
mb = mb->next;
}
}
void dump_scan_info (MEMBLOCK *mb_list)
{
MEMBLOCK *mb = mb_list; while (mb)
{
int i;
printf ("0x%08x %d\r\n", mb->addr, mb->size); for (i = 0; i < mb->size; i++)
{
printf ("%02x", mb->buffer[i]);
}
printf ("\r\n"); mb = mb->next;
}
}
void poke (HANDLE hProc, int data_size, unsigned int addr, unsigned int val)
{
if (WriteProcessMemory (hProc, (void*)addr, &val, data_size, NULL) == 0)
{
printf ("poke failed\r\n");
}
} unsigned int peek (HANDLE hProc, int data_size, unsigned int addr)
{
unsigned int val = 0; if (ReadProcessMemory (hProc, (void*)addr, &val, data_size, NULL) == 0)
{
printf ("peek failed\r\n");
} return val;
}
void print_matches (MEMBLOCK *mb_list)
{
unsigned int offset;
MEMBLOCK *mb = mb_list; while (mb)
{
for (offset = 0; offset < mb->size; offset += mb->data_size)
{
if (IS_IN_SEARCH(mb,offset))
{
unsigned int val = peek (mb->hProc, mb->data_size, (unsigned int)mb->addr + offset);
printf ("0x%08x: 0x%08x (%d) \r\n", mb->addr + offset, val, val);
}
} mb = mb->next;
}
}
int get_match_count (MEMBLOCK *mb_list)
{
MEMBLOCK *mb = mb_list;
int count = 0; while (mb)
{
count += mb->matches;
mb = mb->next;
} return count;
} unsigned int str2int (char *s)
{
int base = 10; if (s[0] == '0' && s[1] == 'x')
{
base = 16;
s += 2;
} return strtoul (s, NULL, base);
}
MEMBLOCK* ui_new_scan(void)
{
MEMBLOCK *scan = NULL;
DWORD pid;
int data_size;
unsigned int start_val;
SEARCH_CONDITION start_cond;
char s[20]; while(1)
{
printf ("\r\nEnter the pid: ");
fgets (s,sizeof(s),stdin);
pid = str2int (s);
printf ("\r\nEnter the data size: ");
fgets (s,sizeof(s),stdin);
data_size = str2int (s);
printf ("\r\nEnter the start value, or 'u' for unknown: ");
fgets (s,sizeof(s),stdin);
if (s[0] == 'u')
{
start_cond = COND_UNCONDITIONAL;
start_val = 0;
}
else
{
start_cond = COND_EQUALS;
start_val = str2int (s);
} scan = create_scan (pid, data_size);
if (scan) break;
printf ("\r\nInvalid scan");
} update_scan (scan, start_cond, start_val);
printf ("\r\n%d matches found\r\n", get_match_count(scan)); return scan;
} void ui_poke (HANDLE hProc, int data_size)
{
unsigned int addr;
unsigned int val;
char s[20]; printf ("Enter the address: ");
fgets (s,sizeof(s),stdin);
addr = str2int (s); printf ("\r\nEnter the value: ");
fgets (s,sizeof(s),stdin);
val = str2int (s);
printf ("\r\n"); poke (hProc, data_size, addr, val);
} void ui_run_scan(void)
{
unsigned int val;
char s[20];
MEMBLOCK *scan; scan = ui_new_scan(); while (1)
{
printf ("\r\nEnter the next value or");
printf ("\r\n[i] increased");
printf ("\r\n[d] decreased");
printf ("\r\n[m] print matches");
printf ("\r\n[p] poke address");
printf ("\r\n[n] new scan");
printf ("\r\n[q] quit\r\n"); fgets(s,sizeof(s),stdin);
printf ("\r\n"); switch (s[0])
{
case 'i':
update_scan (scan, COND_INCREASED, 0);
printf ("%d matches found\r\n", get_match_count(scan));
break;
case 'd':
update_scan (scan, COND_DECREASED, 0);
printf ("%d matches found\r\n", get_match_count(scan));
break;
case 'm':
print_matches (scan);
break;
case 'p':
ui_poke (scan->hProc, scan->data_size);
break;
case 'n':
free_scan (scan);
scan = ui_new_scan();
break;
case 'q':
free_scan (scan);
return;
default:
val = str2int (s);
update_scan (scan, COND_EQUALS, val);
printf ("%d matches found\r\n", get_match_count(scan));
break;
}
}
} int main (int argc, char *argv[])
{
ui_run_scan();
return 0;
}
Link to comment

Hi ragdog,

can you create a exe file with this exsample source for me?Then I can debug it to see better how it works.Yes also I need a memory scanner code to find any specific address somewhere.If you know some already some tools which can do this then tell me so that I can check them.

Ok by the way,so I have make also some test and wrote a little MultiASM code.So would this code be ok or not?Just wanna scan for kernelbases in the almost whole memory which can be located at simple dword counts from section top [00,04,08,0C,etc..].So this code works so far but I have no ideas whether this code could make any problems later which I don't know at the moment.So I just added a AllocationBase check and a STATE MEM_RESERVE check only so is this enough or have I to add more checks etc to prevent a possible exception etc?

<0100739D> ; Load any target and set your patch start addr into <Here> and test;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Simple Memory Scanner of Section top via DWORD count steps from stack to end;; Code starts at label TOP so just enter TOP into expression box;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@KERNEL_STRING:"kernel32.dll\0"@FINDADDR:"\x00\x00\x00\x00"@ADDR:"\x00\x00\x00\x00"@STORE:"\x00\x00\x00\x00"@COUNT:"\x00\x00\x00\x00"@ADDR_WAS:"\x00\x00\x00\x00"@TOP:pushadpush @KERNEL_STRINGcall LoadLibraryAmov dword [@FINDADDR], eaxpush 4push 1000push 10000push 0call VirtualAllocmov dword [@STORE], eaxmov ebx, eaxmov eax,dword ptr fs:[8] ; bottom stackmov [@ADDR], esp@FIND_NOW:call @FINDcmp dword [ebp+4], 00jne short @BASE_THERE@DONT_CHECK:mov edi, [ebp]mov dword [@ADDR_WAS], ediadd edi, [ebp+C]mov dword [@ADDR], edijmp short @FIND_NOW@BASE_THERE:cmp dword [ebp+10], MEM_RESERVEje short @DONT_CHECKmov edi, [ebp]mov eax, dword fs:[18]cmp edi, eaxja short @ENDmov dword [@ADDR], edimov dword [@ADDR_WAS], edimov eax, [ebp+C]add dword [@ADDR], eaxmov edx, 0mov ecx, 4div ecxmov ecx, eaxmov eax, [@FINDADDR]@LOOP:REPNE SCAS DWORD PTR ES:[EDI]    jne short @SEARCH_ENDmov edx, edisub edx, 4                      mov dword [ebx], edxadd ebx, 04inc dword [@COUNT]jmp short @LOOP@SEARCH_END:jmp short @FIND_NOW@FIND:lea ebp,[esp-100]push 1Cpush ebppush [@ADDR]call VirtualQueryret@END:popadnop ; <-- Stop Set BPnopnop
greetz
Link to comment
  • 5 months later...

@LCF-ATAttached compiled binary from ragdog's post C source (small exe size  4KB) easy to debug :)I added also RtlAdjustPrivilege to gain privilege (SE_DEBUG_PRIVILEGE) for Open process (PROCESS_ALL_ACCESS)

main.rar

Edited by Insid3Code
Link to comment

yeah i currently use cheat engine src but its a 60k line of source and very hard to get only the needed 100 line  for scanner. i just need a very simple code that has the sedebugprivilige and can scan for byte array using virtualqueryex:)


thx if you can help


Link to comment
  • 2 weeks later...
  • 9 years later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...