/* (C) 2003-2007 Willem Jan Hengeveld * Web: http://www.xs4all.nl/~itsme/ * http://wiki.xda-developers.com/ * * $Id: pmemmap.cpp 1921 2008-07-31 17:08:31Z itsme $ */ #include #include "ItsUtils.h" #include "dllversion.h" #include "debug.h" #include "args.h" #include #include #include #include #include #include "stringutils.h" #include "ptrutils.h" // // default: // list the kernel address mappings // ( by calling SummarizePageTable and dumping 0x80000000-0xa0000000) // verbose ( -v ) // list current process section // ( by calling SummarizePageTable and dumping 0x0-0xffffffff) // // for one section ( -s 0x.... ) // list virtual address ranges used by section // SummarizeSection(onlyvirtual=true) // verbose for one section ( -v -s 0x.... ) // list virtual to physical address mapping used by section // SummarizeSection(onlyvirtual=false) typedef std::map ProcessInfoMap; // todo: add option to save all sections to files. // todo: add option to produce list of v -> p, p, p ... // todo: create tool to search sections for binary string. // // todo: create streaming interface to read memory, this should greatly improve processing speed. // // maybe add feature to save smaller sections, with max empty space 1M orso. // todo: automatically figure out what area is the sdram. // ( by checking where virtual fffd0000 is mapped ) // bool ITReadProcessMemory(HANDLE hProc, DWORD dwOffset, BYTE *buffer, DWORD dwBytesWanted, DWORD *pdwNumberOfBytesRead); HANDLE ITGetProcessHandle(char *szProcessName); DWORD dwPhys_min= 0x10000000; DWORD dwPhys_max= 0x18000000; DWORD dwVirt_min= 0x00000000; DWORD dwVirt_max= 0x00000000; std::string g_savefilename; FILE *g_f= NULL; bool is_in_psave_range(DWORD dwPhys) { return (dwPhys_min<=dwPhys && dwPhys=0x8000) dwWanted= 0x8000; DWORD nRead; ByteVector data; data.resize(dwWanted); if (!ITReadProcessMemory(NULL, dwStart, vectorptr(data), data.size(), &nRead)) { debug("error reading section data at %08lx\n", dwStart); return false; } if (0!=fseek(g_f, dwStart & 0x1ffffff, SEEK_SET)) perror(stringformat("seek %08lx %s", dwStart & 0x1ffffff, g_savefilename.c_str()).c_str()); size_t n= fwrite(vectorptr(data), 1, data.size(), g_f); if (n!=data.size()) perror(stringformat("writing %s", g_savefilename.c_str()).c_str()); dwLength -= dwWanted; dwStart += dwWanted; } return true; } bool ITReadProcessMemory(HANDLE hProc, DWORD dwOffset, BYTE *buffer, DWORD dwBytesWanted, DWORD *pdwNumberOfBytesRead) { ReadProcessMemoryParams inbuf; DWORD outsize=0; ReadProcessMemoryResult *outbuf=NULL; //debug("ITReadProcessMemory(%08lx %08lx %08lx)\n", hProc, dwOffset, dwBytesWanted); inbuf.hProcess= hProc; inbuf.dwOffset= dwOffset; inbuf.nSize= dwBytesWanted; inbuf.nDataAccess= 0; outbuf= NULL; outsize= 0; HRESULT res= ItsutilsInvoke(L"ITReadProcessMemory", sizeof(ReadProcessMemoryParams), (BYTE*)&inbuf, &outsize, (BYTE**)&outbuf); if (res || outbuf==NULL) { error(res, "ITReadProcessMemory"); return false; } memcpy(buffer, &outbuf->buffer, outbuf->dwNumberOfBytesRead); *pdwNumberOfBytesRead= outbuf->dwNumberOfBytesRead; LocalFree(outbuf); return true; } // NOTE: we assume everything is read with the same handle in one session typedef std::map OffsetBufferMap; OffsetBufferMap cache; bool BufferedReadMemory(HANDLE hProc, DWORD dwOffset, ByteVector **pbuf) { //debug("BufferedReadMemory(%08lx)\n", dwOffset); OffsetBufferMap::iterator i= cache.find(dwOffset&~0xfff); if (i==cache.end()) { ByteVector bv; bv.resize(0x1000); DWORD nRead; if (!ITReadProcessMemory(hProc, dwOffset&~0xfff, vectorptr(bv), bv.size(), &nRead)) return false; cache[dwOffset&~0xfff].swap(bv); i= cache.find(dwOffset&~0xfff); } //debug("setting final: %08lx (size=%x)\n", (*i).first, (*i).second.size()); *pbuf= &((*i).second); return true; } bool CachedITReadProcessMemory(HANDLE hProc, DWORD dwOffset, BYTE *buffer, DWORD dwBytesWanted, DWORD *pdwNumberOfBytesRead) { //debug("CachedITReadProcessMemory(%08lx)\n", dwOffset); if ((dwOffset&~0xfff)!=((dwOffset+dwBytesWanted-1)&~0xfff)) return ITReadProcessMemory(hProc, dwOffset, buffer, dwBytesWanted, pdwNumberOfBytesRead); ByteVector *pbv; if (!BufferedReadMemory(hProc, dwOffset, &pbv)) return false; memcpy(buffer, vectorptr((*pbv))+(dwOffset&0xFFF), dwBytesWanted); return true; } HANDLE ITGetProcessHandle(char *szProcessName) { DWORD insize= (strlen(szProcessName)+1)*sizeof(WCHAR); WCHAR *inbuf= (WCHAR*)LocalAlloc(LPTR, insize); _snwprintf((WCHAR*)inbuf, strlen(szProcessName), L"%hs", szProcessName); inbuf[strlen(szProcessName)]= 0; DWORD outsize=0; HANDLE *outbuf=NULL; HRESULT res= ItsutilsInvoke(L"ITGetProcessHandle", insize, (BYTE*)inbuf, &outsize, (BYTE**)&outbuf); if (res || outbuf==NULL) { error(res, "ITGetProcessHandle"); return INVALID_HANDLE_VALUE; } HANDLE hproc= *outbuf; LocalFree(outbuf); return hproc; } bool GetProcessInfo(bool bIncludeHeap, ProcessInfoMap &pinfo) { GetProcessListParams p; p.bIncludeHeapUsage= bIncludeHeap; DWORD outsize=0; GetProcessListResult *outbuf=NULL; HRESULT res= ItsutilsInvoke(L"ITGetProcessList", sizeof(GetProcessListParams), (BYTE*)&p, &outsize, (BYTE**)&outbuf); if (res || outbuf==NULL) { error(res, "ITGetProcessList"); return false; } if (outsizepe) || outsize < PTR_DIFF(outbuf, &outbuf->pe[outbuf->nEntries])) { debug("INTERNAL ERROR in itsutils.dll: expected %d bytes from ITGetProcessList, got %d\n", PTR_DIFF(outbuf, &outbuf->pe[outbuf->nEntries]), outsize); return false; } for (int i=0 ; inEntries ; i++) { memcpy(&pinfo[(HANDLE)outbuf->pe[i].dwProcessID], &outbuf->pe[i], sizeof(CEPROCESSENTRY)); } LocalFree(outbuf); return true; } DWORD ReadDword(DWORD dwOffset) { DWORD nRead; DWORD dwValue; if (!CachedITReadProcessMemory(NULL, dwOffset, (BYTE*)&dwValue, sizeof(DWORD), &nRead)) return 0; return dwValue; } DWORD FindVirtForPhys(const DWORD *ptbl, DWORD dwPhys) { DWORD dwAddr= 0; DWORD pidx= 0; do { DWORD pe= ptbl[pidx]; if ((pe&3)==2) { if ((pe&0xfff00000)==(dwPhys&0xfff00000)) { return dwAddr | (dwPhys&0xfffff); } } dwAddr+=0x100000; pidx++; } while (dwAddr); return 0; } bool SummarizeTLB2(const DWORD *ptbl1, DWORD dwVirtbase, DWORD dwTlbPhysAddr, DWORD pagesize, int nentries, bool bOnlyVirtual) { DWORD ptbl2ofs= FindVirtForPhys(ptbl1, dwTlbPhysAddr); if (ptbl2ofs==NULL) { debug("ERROR: could not find virtual address for phys %08lx\n", dwTlbPhysAddr); return false; } DwordVector ptbl2; ptbl2.resize(nentries); DWORD nRead; if (!CachedITReadProcessMemory(NULL, ptbl2ofs, (BYTE*)vectorptr(ptbl2), ptbl2.size()*sizeof(DWORD), &nRead)) { debug("error reading tlb2 at %08lx\n", ptbl2ofs); return false; } bool bStarted= false; DWORD dwStartVirt= 0; DWORD dwStartPhys= 0; DWORD dwPrevPhys= 0; DWORD dwAddr= dwVirtbase; DWORD pidx= 0; for (int i=0 ; i p%08lx-%08lx %8x\n", dwStartVirt, dwAddr, dwStartPhys, dwPrevPhys+pagesize, dwAddr-dwStartVirt); bStarted= false; } if (bHasPhysAddr) { if (!bStarted) { bStarted= true; dwStartPhys= dwCurPhys; dwStartVirt= dwAddr; } dwPrevPhys= dwCurPhys; } dwAddr+=pagesize; pidx++; } if (bStarted) { if (bOnlyVirtual) debug("v%08lx-%08lx %8x\n", dwStartVirt, dwAddr, dwAddr-dwStartVirt); else debug("v%08lx-%08lx -> p%08lx-%08lx %8x\n", dwStartVirt, dwAddr, dwStartPhys, dwPrevPhys+pagesize, dwAddr-dwStartVirt); bStarted= false; } return true; } bool SummarizePageTable(bool bVerbose, bool bOnlyVirtual) { DWORD ptbl1ofs= 0xfffd0000; DwordVector ptbl1; ptbl1.resize(4096); DWORD nRead; if (!ITReadProcessMemory(NULL, ptbl1ofs, (BYTE*)vectorptr(ptbl1), ptbl1.size()*sizeof(DWORD), &nRead)) { debug("error reading pagetable at %08lx\n", ptbl1ofs); return false; } DWORD pagesize= 0x100000; bool bStarted= false; DWORD dwStartVirt= 0; DWORD dwStartPhys= 0; DWORD dwPrevPhys= 0; DWORD dwAddr= bVerbose ? 0 : 0x80000000; DWORD pidx= dwAddr/pagesize; do { DWORD pe= ptbl1[pidx]; DWORD dwCurPhys= 0; DWORD dwTlbPhys= 0; DWORD dwTlbPagesize= 0; bool bHasPhysAddr= false; bool bEndBlock= false; bool bHasTlb2= false; switch (pe&3) { case 0: // ignore entry bEndBlock= true; break; case 1: // coarse tlb entry dwTlbPhys= pe&0xfffffc00; dwTlbPagesize= 0x1000; bHasTlb2= true; bEndBlock= true; break; case 2: // section descriptor dwCurPhys= pe&0xfff00000; bHasPhysAddr= true; break; case 3: // fine tlb entry dwTlbPhys= pe&0xfffff000; dwTlbPagesize= 0x400; bHasTlb2= true; bEndBlock= true; break; } if (!bOnlyVirtual && dwCurPhys-dwPrevPhys!=pagesize) bEndBlock= true; if (bStarted && bEndBlock) { if (bOnlyVirtual) debug("v%08lx-%08lx %8x\n", dwStartVirt, dwAddr, dwAddr-dwStartVirt); else debug("v%08lx-%08lx -> p%08lx-%08lx %8x\n", dwStartVirt, dwAddr, dwStartPhys, dwPrevPhys+pagesize, dwAddr-dwStartVirt); bStarted= false; } if (bHasTlb2) { SummarizeTLB2(vectorptr(ptbl1), dwAddr, dwTlbPhys, dwTlbPagesize, pagesize/dwTlbPagesize, bOnlyVirtual); } if (bHasPhysAddr) { if (!bStarted) { bStarted= true; dwStartPhys= dwCurPhys; dwStartVirt= dwAddr; } dwPrevPhys= dwCurPhys; } dwAddr+=pagesize; pidx++; if (!bVerbose) { if (dwAddr==0xa0000000) { dwAddr= 0xe0000000; pidx += 0x40000000/pagesize; } } } while (dwAddr); if (bStarted) { if (bOnlyVirtual) debug("v%08lx-%08lx %8x\n", dwStartVirt, dwAddr, dwAddr-dwStartVirt); else debug("v%08lx-%08lx -> p%08lx-%08lx %8x\n", dwStartVirt, dwAddr, dwStartPhys, dwPrevPhys+pagesize, dwAddr-dwStartVirt); bStarted= false; } return true; } bool SummarizeSection(DWORD dwSectionAddr, bool bOnlyVirtual) { DWORD sectionTableOfs= (dwSectionAddr<0x80000000) ? 0xFFFFC8A0 : 0xFFFFCB6C; //debug("sectiontable %08lx\n", sectionTableOfs); DwordVector SectionTable; SectionTable.resize((dwSectionAddr<0x80000000) ? 64 : 1); DWORD nRead; if (!CachedITReadProcessMemory(NULL, sectionTableOfs, (BYTE*)vectorptr(SectionTable), SectionTable.size()*sizeof(DWORD), &nRead)) { debug("error reading sectiontable at %08lx\n", sectionTableOfs); return false; } DWORD pagesize= 0x1000; #define VA_BLOCK 16 #define VA_SECTION 25 #define VA_PAGE 12 #define BLOCK_MASK 0x1ff #define PAGE_MASK 0x00f #define NULL_BLOCK 0 #define RESERVED_BLOCK 1 DWORD dwSectionOfs= SectionTable[(dwSectionAddr<0x80000000) ? dwSectionAddr>>VA_SECTION : 0]; //debug("section %08lx\n", dwSectionOfs); DwordVector section; section.resize(512); if (!CachedITReadProcessMemory(NULL, dwSectionOfs, (BYTE*)vectorptr(section), section.size()*sizeof(DWORD), &nRead)) { debug("error reading section at %08lx\n", dwSectionOfs); return false; } DWORD dwStartPhys=0; DWORD dwStartVirt=0; DWORD dwPrevPhys=0; bool bStarted= false; DWORD dwVAddr= 0; for (DWORD ixBlock= 0 ; ixBlock p%08lx-%08lx %8x\n", dwStartVirt, dwVAddr, dwStartPhys, dwPrevPhys+pagesize, dwVAddr-dwStartVirt); bStarted= false; } } else { //debug("memblock %08lx\n", dwMemblockOfs); DwordVector memblock; memblock.resize(4); if (!CachedITReadProcessMemory(NULL, dwMemblockOfs, (BYTE*)vectorptr(memblock), memblock.size()*sizeof(DWORD), &nRead)) { debug("error reading memblock at %08lx\n", dwMemblockOfs); return false; } DWORD dwPagesOfs= memblock[3]; //debug("pagelist %08lx\n", dwPagesOfs); DwordVector pages; pages.resize(16); if (!CachedITReadProcessMemory(NULL, dwPagesOfs, (BYTE*)vectorptr(pages), pages.size()*sizeof(DWORD), &nRead)) { debug("error reading memblock pagelist at %08lx\n", dwPagesOfs); return false; } for (DWORD ixPage= 0 ; ixPage p%08lx-%08lx %8x\n", dwStartVirt, dwVAddr, dwStartPhys, dwPrevPhys+pagesize, dwVAddr-dwStartVirt); if (!g_savefilename.empty() && is_in_psave_range(dwStartPhys) && is_in_vsave_range(dwStartVirt)) save_to_section_file(dwStartVirt, dwVAddr-dwStartVirt); bStarted= false; } } else { DWORD dwPhys= pages[ixPage]&0xfffff000; if (!bOnlyVirtual && dwPhys-dwPrevPhys!=pagesize) { if (bStarted) { if(bOnlyVirtual) debug("v%08lx-%08lx %8x\n", dwStartVirt, dwVAddr, dwVAddr-dwStartVirt); else debug("v%08lx-%08lx -> p%08lx-%08lx %8x\n", dwStartVirt, dwVAddr, dwStartPhys, dwPrevPhys+pagesize, dwVAddr-dwStartVirt); if (!g_savefilename.empty() && is_in_psave_range(dwStartPhys) && is_in_vsave_range(dwStartVirt)) save_to_section_file(dwStartVirt, dwVAddr-dwStartVirt); bStarted= false; } } if (!bStarted) { bStarted= true; dwStartPhys= dwPhys; dwStartVirt= dwVAddr; } dwPrevPhys= dwPhys; } dwVAddr+=pagesize; } } } if (bStarted) { if(bOnlyVirtual) debug("v%08lx-%08lx %8x\n", dwStartVirt, dwVAddr, dwVAddr-dwStartVirt); else debug("v%08lx-%08lx -> p%08lx-%08lx %8x\n", dwStartVirt, dwVAddr, dwStartPhys, dwPrevPhys+pagesize, dwVAddr-dwStartVirt); if (!g_savefilename.empty() && is_in_psave_range(dwStartPhys) && is_in_vsave_range(dwStartVirt)) save_to_section_file(dwStartVirt, dwVAddr-dwStartVirt); bStarted= false; } return true; } bool SummarizeAllSections(bool bOnlyVirtual) { for (DWORD dwSection= 0x02 ; dwSection<0xC4 ; dwSection!=0x7E ? dwSection += 2 : dwSection = 0xC2) { if (!SummarizeSection(dwSection*0x1000000, bOnlyVirtual)) return false; } if (!SummarizePageTable(true, bOnlyVirtual)) return false; return true; } DWORD GetProcessSectionSlot(HANDLE hProc) { ProcessInfoMap pmap; if (GetProcessInfo(false, pmap)) if (pmap.find(hProc)!=pmap.end()) return pmap[hProc].dwMemoryBase; return 0xffffffff; } void usage() { printf("(C) 2003-2008 Willem jan Hengeveld itsme@xs4all.nl\n"); printf("Usage: pmemmap [-v] [-s SECTION | -n NAME | -h HANDLE] [-w file] [-p MIN-MAX]\n"); printf(" -p specifies the range of phys addrs to save to savefile\n"); printf(" -a lists all sections\n"); printf(" -V MIN-MAX restricts what virtual addresses of the section are saved.\n"); } int main( int argc, char *argv[]) { bool bVerbose= false; bool bSummarizeAll= false; DWORD dwSectionAddr=0xFFFFFFFF; char *szProcessName= NULL; HANDLE hProcess= INVALID_HANDLE_VALUE; std::string vminmaxstring; std::string pminmaxstring; DebugStdOut(); for (int i=1 ; i