justdream Posted September 25, 2011 Posted September 25, 2011 (edited) 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;beginif (s <> '') and (s[1] <> '$') then result := strToInt64('$' + s )else result := strToInt64(s);end;procedure FirstScan(valor: string);vardwEndAddr : 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 September 25, 2011 by justdream
atom0s Posted September 25, 2011 Posted September 25, 2011 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#p11102I'm not a Delphi coder so I can't say that your issue is caused by any coding errors, sorry.
BoB Posted September 25, 2011 Posted September 25, 2011 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
LCF-AT Posted October 24, 2013 Posted October 24, 2013 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 00000000So 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
kao Posted October 25, 2013 Posted October 25, 2013 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)
LCF-AT Posted October 26, 2013 Posted October 26, 2013 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 sizeStrange 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
ragdog Posted October 26, 2013 Posted October 26, 2013 Hi Here is a good example from TEDSONOr 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; }
LCF-AT Posted October 26, 2013 Posted October 26, 2013 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 BPnopnopgreetz
tbeclock Posted April 15, 2014 Posted April 15, 2014 could someone provide a delphi source for memory search for running processes please?
atom0s Posted April 15, 2014 Posted April 15, 2014 Look into Cheat Engine's source if you want a Delphi memory scanning source. https://code.google.com/p/cheat-engine/
Insid3Code Posted April 15, 2014 Posted April 15, 2014 (edited) @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 April 15, 2014 by Insid3Code
tbeclock Posted April 16, 2014 Posted April 16, 2014 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
tbeclock Posted April 28, 2014 Posted April 28, 2014 do u guys know some simple good delphi scanner for array bytes? In my example i would like to read '50' bytes.
boot Posted July 4, 2023 Posted July 4, 2023 RtlAdjustPrivilege is an interesting undocumented function. It is also one of the ways to improve debugging privilege.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now