No announcement yet.

Dumping process module from memory to disk

  • Filter
  • Time
  • Show
Clear All
new posts

  • Dumping process module from memory to disk

    Steve Hutchesson, you may be the person to answer this but all feedback is welcome! How do programs such as ProcDump dump modules from memory to disk? Is this an accomplishable task in PB, and are there any examples of this nature?


  • #2
    I rephrase my question - I'll pay for somebody to write me the source code to dump process modules from memory to disk. Normally i like coding everything myself but there are some things i dont think i'd be able to achieve in a reasonable timeframe due to the research id have to put in beforehand
    perhaps there should be a Jobs forum for questions like this? If anybody is interested, could they please reply here or mail to [email protected]



    • #3

      Following is a rather crude technique for getting a loaded PE image from
      another process. It allocates a buffer that is much larger than the
      expected loaded image size which is filled with a single character. I have
      done it this way to avoid reading the section data from the PE header as
      it can be destroyed by various protection schemes.

      It reads from hex 400000 (&H400000) which is the normal start address and
      continues to read until the OS denies access to any further read.

      You then search the buffer for the first occurrence of a long repeat of
      the fill character which gives you the end of the loaded image.

      Point a non loaded disk file name at the function and write the return
      string to disk and you have a raw PE memory dump. NOTE that it will not
      run in this form.

      This function is valid for Win95b and it uses documented access but with
      the endless changes in 32 bit windows, there is no garrantee that it will
      run on either NT4 or WIN2K.


      [email protected]

        FUNCTION GetPEimage(fname$,ByVal LoadAdr as DWORD) as STRING
            LOCAL hMem  as DWORD
            LOCAL bRead as DWORD
            LOCAL EndIm as DWORD
            LOCAL hProc as DWORD
            LOCAL bSize as DWORD
            LOCAL fl    as DWORD
            LOCAL pi    as PROCESS_INFORMATION
            LOCAL si    as STARTUPINFO
            LOCAL buffer$
            Open fname$ for Binary as #1
              fl = lof(1)
              Get$ #1,1000, a$
              If instr(a$,"PE"+chr$(0,0)) = 0 Then
                MessageBox hWnd,"Cannot find PE signature in header", _
                                "Not PE file",%MB_OK
                FUNCTION = ""
                Exit FUNCTION
              End If
            Close #1
            rvP& = CreateProcess(ByVal StrPtr(fname$), _
                                 ByVal %NULL, _
                                 ByVal %NULL, _
                                 ByVal %NULL, _
                                 %FALSE, _
                                 %DETACHED_PROCESS, _
                                 ByVal %NULL, _
                                 ByVal %NULL, _
                                 ByVal VarPtr(si), _
                                 ByVal VarPtr(pi))
            hProc = OpenProcess(%PROCESS_VM_READ,%TRUE,pi.dwProcessId)
            bSize = fl * 6
            buffer$ = string$(bSize,"x")
            If LoadAdr = 0 Then
              LoadAdr = &H400000   ' default 4 meg load address
            End If
            hMem = ReadProcessMemory(hProc,LoadAdr,ByVal StrPtr(buffer$), _
                                     bSize,ByVal VarPtr(bRead))
            EndIm = instr(buffer$,"xxxxxxxxxxxx") ' locate the end of the buffer
            ! dec EndIm
            CloseHandle hProc
            FUNCTION = left$(buffer$,EndIm)
      ' #########################################################################
      hutch at movsd dot com
      The MASM Forum


      • #4
        Steve, youre sure I cant buy you a drink or anything?
        Your "rather crude" technique works SUPERBLY under Windows NT (havent tested 95/98, but i dont see why they wouldnt work after success on NT and you developing it under 98)
        Its unfortunate I can't dump processes that are already running, but this is still a huge step forward in my learning! Thankyou very very much
        Drinks on me (!)



        • #5

          I just got an answer from one of the authors of ProcdumpNT, it reconstructs
          the sections in a PE file directly from the disk file so the process of
          overwriting the PE header does not effect its capacity to get a complete PE

          It is in fact the only way to do it unless the unpacker wants to try a
          heuristic technique of reading the complete image and calculating where the
          addresses start.


          [email protected]

          PS, next time you are in Sydney, I am a pure malt man so you can shout me a
          nip of Glenfiddich.

          hutch at movsd dot com
          The MASM Forum


          • #6
            Glenfiddich it is ! (- ive still got half a bottle )
            im in Perth by the way - good luck with the games, i hope it doesn't invade your suburb :-)

            I have one final question regarding processes/memory before I can carry on fluently with the rest of my project. I am trying to determine the base address... you are defaulting to &H400000, but I am trying to determine this value the proper way, however that may be :-)

            This is what ive got... it compiles, but i dont think im calling things right... (is this the right call to make to determine base address?)
            naturally i am calling it after using OpenProcess, and parsing the handle over to NtQueryInformationProcess

            DECLARE FUNCTION QueryInformationProcess LIB "ntdll.dll" ALIAS "NtQueryInformationProcess" (BYVAL ProcessHandle AS LONG, BYVAL inProcInfoClass AS LONG, BYVAL pBuffer AS ANY, PROCLENGTH AS LONG) AS LONG

            DIM xRes AS LONG
            DIM xLen AS LONG
            xLen = 255
            DIM xMyInfo AS ProcessBasicInformation
            xInfoType = xMyInfo
            xRes = QueryInformationProcess(BYVAL hProc, BYVAL xInfoType, BYVAL VARPTR(iProc), xLen)
            END FUNCTION



            • #7
              there is an example with C++ source at and i am
              sure this is the solution (at least under NT), but Im having a lot of
              trouble converting C++ to PB ,as i dont write in C/C++ .. can you embed C in PB code? (i read on one forum here that you could, but i was only aware of basic and asm?), or is someone wise and kind enough to translate it to PB?



              • #8

                The only safe way to get the starting address in an EXE file is to read
                it from the disk file and even this is subject to possible change.

                The hInstance passed to an EXE file is the address of the beginning of
                the PE image so in your own EXE files, dumping the PE image to disk is
                easy, just read from hInstance for as much as you need.

                I have yet to find a PE EXE that has a different load address to &H400000
                and any that I have tried to rebase will not run but the capacity exists
                so it is worth chasing what you are after.

                The API you are using is not available in win9x and my MSDN does not have
                it either so I don't have any way of testing what it will return.

                I have had to write my own set of PE image headers from my MASM include
                file to get a reliable set of structures to work on PE files and this
                gives reasonably straight forward access to the information stored in the
                different headers in a PE file.


                [email protected]

                hutch at movsd dot com
                The MASM Forum



                • #9
                  hi wayne

                  the example you posted while very interesting is very long and
                  does not correctly retrieve the base address of the open process.

                  i don't have the time to translate it pb at the moment - however
                  i posted some code a little while ago for a rebase utility which
                  correctly retrieves the base address of a executable/dll.

                  you can find it at:

                  extract what you need from the code and look at semen's code which
                  shows how to retrieve all running processes - this will get you
                  the processes' handles.

                  you can find it at:

                  combine that with hutch's code and you've got a winner





                  • #10

                    Below is the include file that I am using to work on PE files. I have
                    slightly renamed the structures so that the names do not clash with the
                    ones in the standard include file with PB6. This is are a straight port
                    from my MASM32 include file and it tests OK in PowerBASIC.

                    I will try and post some decent reference material on PE files, there
                    is a reasonably good text file by LUEVELSMEYER and a very comprehensive
                    PDF file called WINF10.PDF but neither seems to be readily available on
                    the Internet at the moment.

                    For dumping a PE image, probably the only safe way is to get the header
                    information from the disk file as it can be modified by the application.

                    I have not had a chance to have a look at Semen code but I just had a
                    quick look at the code posted by Florent for rebasing DLLs which is very
                    good and this stuff, particularly the imagehlp.dll functions should be
                    very useful to you.


                    [email protected]

                      ' ************************************
                      ' Structures for PE file manipulation
                      ' ************************************
                      %IMAGE_SIZEOF_SECTION_HEADER      = 40
                      ' ---------------------------------
                      ' original name "IMAGE_DOS_HEADER"
                      ' ---------------------------------
                      TYPE IMAGE_DOS_HDR
                        e_magic                       as WORD
                        e_cblp                        as WORD
                        e_cp                          as WORD
                        e_crlc                        as WORD
                        e_cparhdr                     as WORD
                        e_minalloc                    as WORD
                        e_maxalloc                    as WORD
                        e_ss                          as WORD
                        e_sp                          as WORD
                        e_csum                        as WORD
                        e_ip                          as WORD
                        e_cs                          as WORD
                        e_lfarlc                      as WORD
                        e_ovno                        as WORD
                        e_res(3)                      as WORD '  4 member WORD array
                        e_oemid                       as WORD
                        e_oeminfo                     as WORD
                        e_res2(9)                     as WORD ' 10 member WORD array
                        e_lfanew                      as LONG
                      END TYPE
                      ' ----------------------------------
                      ' original name "IMAGE_FILE_HEADER"
                      ' ----------------------------------
                      TYPE IMAGE_FILE_HDR
                        Machine                       as WORD
                        NumberOfSections              as WORD
                        TimeDateStamp                 as DWORD
                        PointerToSymbolTable          as DWORD
                        NumberOfSymbols               as DWORD
                        SizeOfOptionalHeader          as WORD
                        Characteristics               as WORD
                      END TYPE
                      ' --------------------------------------
                      ' original name "IMAGE_DATA_DIRECTORY"
                      ' --------------------------------------
                      TYPE IMAGE_DATA_DIR
                        VirtualAddress                as DWORD
                        isize                         as DWORD
                      END TYPE
                      ' --------------------------------------
                      ' original name "IMAGE_OPTIONAL_HEADER32"
                      ' --------------------------------------
                      TYPE IMAGE_OPTIONAL_HDR
                        Magic                         as WORD 
                        MajorLinkerVersion            as BYTE 
                        MinorLinkerVersion            as BYTE 
                        SizeOfCode                    as DWORD
                        SizeOfInitializedData         as DWORD
                        SizeOfUninitializedData       as DWORD
                        AddressOfEntryPoint           as DWORD
                        BaseOfCode                    as DWORD
                        BaseOfData                    as DWORD
                        ImageBase                     as DWORD
                        SectionAlignment              as DWORD
                        FileAlignment                 as DWORD
                        MajorOperatingSystemVersion   as WORD 
                        MinorOperatingSystemVersion   as WORD 
                        MajorImageVersion             as WORD 
                        MinorImageVersion             as WORD 
                        MajorSubsystemVersion         as WORD 
                        MinorSubsystemVersion         as WORD 
                        Win32VersionValue             as DWORD
                        SizeOfImage                   as DWORD
                        SizeOfHeaders                 as DWORD
                        CheckSum                      as DWORD
                        Subsystem                     as WORD 
                        DllCharacteristics            as WORD 
                        SizeOfStackReserve            as DWORD
                        SizeOfStackCommit             as DWORD
                        SizeOfHeapReserve             as DWORD
                        SizeOfHeapCommit              as DWORD
                        LoaderFlags                   as DWORD
                        NumberOfRvaAndSizes           as DWORD
                        DataDirectory(15)             as IMAGE_DATA_DIR   ' 16 member array of structures
                      END TYPE
                      ' ---------------------------------
                      ' original name "IMAGE_NT_HEADERS"
                      ' ---------------------------------
                      TYPE IMAGE_NT_HDR
                        Signature                     as DWORD
                        FileHeader                    as IMAGE_FILE_HDR
                        OptionalHeader                as IMAGE_OPTIONAL_HDR
                      END TYPE
                      ' ---------------------------------------
                      ' original name "IMAGE_EXPORT_DIRECTORY"
                      ' ---------------------------------------
                      TYPE IMAGE_EXPORT_DIR
                        Characteristics               as DWORD
                        TimeDateStamp                 as DWORD
                        MajorVersion                  as WORD
                        MinorVersion                  as WORD
                        nName                         as DWORD
                        nBase                         as DWORD
                        NumberOfFunctions             as DWORD
                        NumberOfNames                 as DWORD
                        AddressOfFunctions            as DWORD
                        AddressOfNames                as DWORD
                        AddressOfNameOrdinals         as DWORD
                      END TYPE
                      UNION MISC
                        PhysicalAddress               as DWORD
                        VirtualSize                   as DWORD
                      END UNION
                      ' ---------------------------------------
                      ' original name "IMAGE_SECTION_HEADER"
                      ' ---------------------------------------
                      TYPE IMAGE_SECTION_HDR
                        Name1                         as STRING * 8
                        Property                      as MISC
                        VirtualAddress                as DWORD
                        SizeOfRawData                 as DWORD
                        PointerToRawData              as DWORD
                        PointerToRelocations          as DWORD
                        PointerToLinenumbers          as DWORD
                        NumberOfRelocations           as WORD
                        NumberOfLinenumbers           as WORD
                        Characteristics               as DWORD
                      END TYPE

                    hutch at movsd dot com
                    The MASM Forum



                    • #11
                      Here are the two files I have as PE reference,

                      LUEVELSMEYER's example has a C program and a good text file
                      with it to explain the PE file format.

                      The PDF file originated from the Intel site a couple of years
                      ago and is a very comprehensive document on PE format.

                      [email protected]

                      hutch at movsd dot com
                      The MASM Forum



                      • #12
                        Hi Hutch

                        very interesting info - I used the pe_map.exe to dump
                        hello.exe and got a nice informative report. I'll study
                        the LUEVESMEYER source as soon as I have time.

                        I read pe.txt which is very informative. I very much liked the
                        section where he shows making hello.exe executable. Can you
                        run Hello.exe on your system? On mine, NT 4.0 SP6 I get a
                        critical error message box telling me hello.exe is not a valid
                        NT application.



                        [This message has been edited by Florent Heyworth (edited September 10, 2000).]


                        • #13

                          It did not run here either on win95b, incorrent alignment so I had a look
                          at it in a hex editor and it does not have a valid MZ header. I think both
                          NT4 and 9x will not run a file with an invalid MZ header or that is aligned
                          below 512 bytes.

                          From my experience, the smallest I have been able to get a valid PE file
                          is 1024 bytes with a test piece in MASM. It normally builds at 1536 bytes
                          but by merging the 2 sections with the /MERGE linker option, it drops in
                          size by a single 512 byte alignment.

                          Also thanks for posting the rebase code, it will be very useful.


                          [email protected]

                          hutch at movsd dot com
                          The MASM Forum



                          • #14
                            Steve and Florent, thanks very much for your help! It is much appreciated as i am swimming in deep water here and Im not wearing floaties :-)
                            Im getting closer to what I want to do (which is just dump a processes main module to a file), and its working on some , but not others...
                            Steve i modified the example you supplied up the top so that it can dump from an already-running process rather than having to start one up (parsing the process id)
                            FUNCTION GetPEimage(ProcID AS LONG, BYVAL LoadAdr AS DWORD) AS STRING
                                  LOCAL hMem  AS DWORD
                                  LOCAL bRead AS DWORD
                                  LOCAL EndIm AS DWORD
                                  LOCAL hProc AS DWORD
                                  LOCAL bSize AS DWORD
                                  LOCAL tSize AS DWORD
                                  LOCAL fl    AS DWORD
                                  LOCAL pi    AS PROCESS_INFORMATION
                                  LOCAL si    AS STARTUPINFO
                                  LOCAL buffer$
                                  LOCAL hWnd AS LONG
                                  hProc = OpenProcess(%PROCESS_VM_READ,%TRUE,ProcID)
                                  bSize = 2000000 'almost 2 megs to dump
                                  tbuffer$ = "  "
                                  buffer$ = STRING$(bSize,"x")
                                  IF LoadAdr = 0 THEN
                                    LoadAdr = &H400000   'default 4 meg load address
                                  END IF
                                  hMem = ReadProcessMemory(hProc,LoadAdr,BYVAL STRPTR(buffer$),bSize,BYVAL VARPTR(bRead))
                                  EndIm = INSTR(buffer$,"xxxxxxxxxxxx") 'locate the end of the buffer
                                  ! dec EndIm
                                  CloseHandle hProc
                                  FUNCTION = LEFT$(buffer$,EndIm)
                              END FUNCTION
                            so that part is basically working perfectly on Windows 95, 98, NT , _AND_ 2000 so Steve I am over the moon that you have helped me get that far
                            but I am getting a headache from this base address issue. Im assuming that the reason it can only dump some processes and not all is because the base address isnt the default

                            I also tried scanning for the string "This program" (eg. for "This program cannot run in DOS") starting at 1 and working my way up to 256mb yet was unable to locate it even in processes such as Notepad. Scanning in that way should have worked with a process such as Notepad, i would've thought? even if it wouldn't work with an exe that has been "header-munted" by Steve's other example
                            getting there!



                            • #15
                              thought this may be of interest while you were mentioning smallest exe files ...
                              It is a remote access trojan server that is a standalone total exe of 3072 bytes. It works on 95, 98, NT, and 2000, and gives anyone who connects to it (tcp 7777) a command prompt - now thats fat-free programming :-)



                              • #16
                                Steve or Florent or anyone! have you had any luck in getting all processes to dump ok? im still only able to get a few to dump! would this be because the base address may not be &H400000 ? i can only dump about one in five processes though, so that doesnt seem to be it?



                                • #17

                                  There can be a number of reasons, 400000h works on most PE EXE files but
                                  it will not work on a DLL, they are dynamically loaded by the operating
                                  system loader at different addresses in the application's address space
                                  depending on the address range available. From memory, there is an API
                                  call designed to get a DLL starting address (Module handle).

                                  There are of course a number of tricks used as both anti-debugging and
                                  anti-hacking measures that will stop a successful image dump. Another
                                  consideration is that from memory, this process cannot be used on non-PE
                                  files. The original function I posted was designed to work on programs
                                  started with CreateProcess and it seems to do the job on the PE EXE files
                                  that I have used it with but for serious image analysis, I have mainly
                                  seen guys using SICE for NT with an add-on called Icedump.

                                  Set a breakpoint at the program entry point then dump the PE image. There
                                  are specialised techniques to defeat this method but there are as many
                                  trick around it.


                                  [email protected]

                                  hutch at movsd dot com
                                  The MASM Forum



                                  • #18
                                    Steve, I just realised that you do your work on 95/98 and not NT as I do - I finally was able to try your code on a 98 box tonight and it works perfectly for ALL the processes that I tried, so youre code is spot on ! ! im puzzled why it's only able to dump a minority of NT/2K processes though :/, but some is better than none
                                    Again, many thanks for all your help with this issue Steve, ive learnt a lot from your postings and should be able to go on from this!



                                    • #19
                                      Steve, I can now successfully dump in NT/2K! ... the secret was to use the AttachThreadInput API first! I would not have been able to discover that if it wasn't for your help, so I return today stoked that I can at least partially return your kind favour.
                                      Many thanks again!
                                      PS. Upgrade to NT! <grin>