0

So i have been using urlmon.dll's help with getting the MIME type of files' data as suggested in This answer, and its been working fine in windows 7.

However, on windows 10 the same code generates System.AccessViolationException when trying to create a string from the mime pointer.

this is the problematic code:

uint mimeType;
FindMimeFromData(0, null, data, 256, null, 0, out mimeType, 0);
var mimePointer = new IntPtr(mimeType);
//Exception is thrown on the next line
var mime = Marshal.PtrToStringUni(mimePointer);

The code works fine on windows 7 and on the same files, however when running this on windows 10 i suddenly get Access Violation.

Did anyone else encounter this error ?

Banana
  • 7,424
  • 3
  • 22
  • 43
  • have you ran it in administration mode?, also is there a reason you cant get the `mimetype` from the files extension? – traveler3468 Jun 26 '18 at 07:42
  • @AndrewE yes i have tried running as administrator and it doesnt work (wouldnt be a solution either because normal users are going to use it on company computers, with no admin privileges). and i do take extension into consideration, but many times the file types i work with dont match the extension (blame our customers) and i prefer to check it myself. – Banana Jun 26 '18 at 07:52
  • Windows 10 was released 3 years ago so this isn't a "did anyone else encounter this" error. It's the only supported Windows version now. The buffer may be too small, or the pointer is the wrong type, or you may have used the 32-bit version of the DLL on a 64-bit machine. At the very least, post the full exception including its call stack. You can get it easily with `Exception.ToString()`. – Panagiotis Kanavos Jun 26 '18 at 07:59
  • @Banana just to try, have you tried changing the `Platform Target` to x64 – traveler3468 Jun 26 '18 at 08:09
  • 1
    @AndrewE yes i have, in fact it was the first thing i tried but to no avail – Banana Jun 26 '18 at 08:12
  • just to try, (not a solution) what happens if you run the exe in Windows 7 compatibility mode? – traveler3468 Jun 26 '18 at 08:14
  • @AndrewE tried this too, same problem. – Banana Jun 26 '18 at 08:16
  • When you say you can't get the mimetype from its extension, where you accessing the registry to get it? – traveler3468 Jun 26 '18 at 08:18

1 Answers1

4
uint mimeType;
FindMimeFromData(0, null, data, 256, null, 0, out mimeType, 0);
var mimePointer = new IntPtr(mimeType);

This is surely wrong at 64 bits... A IntPtr is 64 bits (it is a memory address)... How could a uint (a 32 bits) contain it?

And if we take a look at the pinvoke site the signature should be:

[DllImport("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)]
static extern int FindMimeFromData(IntPtr pBC,
    [MarshalAs(UnmanagedType.LPWStr)] string pwzUrl,
    [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.I1, SizeParamIndex=3)] 
    byte[] pBuffer,
    int cbSize,
    [MarshalAs(UnmanagedType.LPWStr)] string pwzMimeProposed,
    int dwMimeFlags,
    out IntPtr ppwzMimeOut,
    int dwReserved);

very important while the documentation of the method is very poor on msdn, calling FindMimeFromData will cause a memory leak: you have to free the ppwzMimeOut you receive... The problem is that it isn't clear how: here it is suggested to use CoTaskMemFree, that is Marshal.FreeCoTaskMem. I'll say that it is right, tested with:

byte[] bytes = File.ReadAllBytes("someimage.jpg");

while (true)
{
    IntPtr ptr1;
    int success1 = FindMimeFromData(IntPtr.Zero, null, bytes, bytes.Length, null, 0, out ptr1, 0);
    Marshal.FreeCoTaskMem(ptr1);
}

If I remove the Marshal.FreeCoTaskMem and take a look with the TaskManager, the memory used by the process will go up quite quickly... If I restore the Marshal.FreeCoTaskMem, the memory will remain stable.

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • your point makes sense, but it worked just fine on windows 7 64bit, whats different in windows 10? – Banana Jun 26 '18 at 08:04
  • @Banana Not all 64 bits addresses need 64 bits... Some stay under 4gb and so are castable to 32 bit. But clearly it is random – xanatos Jun 26 '18 at 08:08
  • the computer im testing this on has 4gb of ram so there are no qualifying addresses to cause this problem – Banana Jun 26 '18 at 08:11
  • @Banana There is a difference between physical memory address and virtual memory address. Your 4gb of memory can be mapped to any address. – xanatos Jun 26 '18 at 08:17
  • 2
    @Banana If I have to make an educated guess, I'll say that the changes Microsoft made to `ASLR` (Address Space Layout Randomization) between Windows 7 and Windows 10 caused memory allocations to go over the 4gb "border" of virtual space. One of the possible randomizations is about the bottom-up memory allocation (see [here](https://blogs.technet.microsoft.com/srd/2013/12/11/software-defense-mitigating-common-exploitation-techniques/)), and I'll say that *that* is what caused the memory address to go from "very low" (< 4gb) to "random" (> 4gb) – xanatos Jun 26 '18 at 08:30
  • i just tested the code with the declaration you suggested and using `IntPtr` instead of `Uint` and it works perfectly, thank you! – Banana Jun 26 '18 at 08:38
  • @Banana Note what I've added at the end of the response: you must free the memory received my `FindMimeFromData` – xanatos Jun 26 '18 at 08:39
  • yes i haven't included it in the question's code but i am freeing the memory. had to figure it out the hard way... – Banana Jun 26 '18 at 08:42