-
Notifications
You must be signed in to change notification settings - Fork 450
[NtTypes] Correctly compute buffer size allocated for ObjectTypesInformation #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[NtTypes] Correctly compute buffer size allocated for ObjectTypesInformation #18
Conversation
|
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. |
1277e67 to
b752356
Compare
|
Hmm, wow, okay. Wouldn't surprise me if there's a bug in the wow64 layer (there's a few of those) as regardless of whether the return length value is correct or not (seems fine on 64bit) it shouldn't end up overwriting the end of the allocated size we passed in to begin with. I have noticed there seemed to be a memory corruption somewhere in 32 bit, wonder if this is it? Can you fix the CLA issue? Also I have at least one comment on the commit to resolve. |
NtApiDotNet/NtType.cs
Outdated
| private static int GetTypeSize() | ||
| { | ||
| using (var type_info = new SafeStructureInOutBuffer<ObjectAllTypesInformation>()) | ||
| int size = 1000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just roll this all into LoadTypes()? Seems unnecessary to calculate the length in a loop then actually do then just return that size and drop all the type information we gathered just to do it again. Perhaps cap at max of around 1MiB for buffer size and start with size of say 8192 bytes as for all current versions of Windows that should result in never having to loop at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I kept every changes in GetTypeSize() in order for the PR to be simpler, but it makes every sense to move it back to LoadTypes(). Since only LoadTypes calls GetTypeSize, we might as well get rid of GetTypeSize.
NtApiDotNet/NtType.cs
Outdated
| return GetTypeByType<T>(true); | ||
| } | ||
|
|
||
| /// <summary> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't really need a comment on an private method.
NtApiDotNet/NtType.cs
Outdated
| } | ||
|
|
||
| // Double the size of the reception buffer until the API is okay with it. | ||
| // TODO : Prevent this exponential increase from grabbing all the RAM by capping it to a large value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As said perhaps cap at around 1MiB as I can't imagine it ever going close to that sort of size. If we can extract type information something more serious is going wrong.
b752356 to
b6c64fd
Compare
|
CLAs look good, thanks! |
…rmation `ntdll.NtQueryObject` is one in a long list of natives API with a pretty funny way to use it. It takes a buffer and a buffer size, and on success fills out the bufffer and write the written length in a return_length parameter. However if the buffer is not big enough, the return status is set to STATUS_INFO_LENGTH_MISMATCH inciting the developer to embiggen the input buffer and re-attempt the call. However return_length parameter is also sometimes set but it should not be relied upon as long as status is not equal to STATUS_SUCCESS.
Since we do a pretty complicate dance in order to fill out a buffer with ObjectTypesInformation data, we might as well do it once and use the result right away. The CFG of LoadTypes is clearly not simple, and moreso since we use a 'using' statement to automatically liberate local heap buffers.
NtApiDotNet/NtType.cs
Outdated
| return ret; | ||
|
|
||
| default: | ||
| status.ToNtException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Best replace this with a "throw new NtException(status);" as ToNtException only throws on error codes but we want to throw for everything.
NtApiDotNet/NtType.cs
Outdated
| NtStatus status = NtStatus.STATUS_INFO_LENGTH_MISMATCH; | ||
|
|
||
| // repeatly try to fill out ObjectTypes buffer by increasing it's size between each attempt | ||
| while (status == NtStatus.STATUS_INFO_LENGTH_MISMATCH) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd replace the while loop with a check against the size (i.e. size < 0x100000) then move the definition of status of the dictionary into the loop. Then you can replace the redundant return with a "throw new NtException(NtStatus.STATUS_INSUFFICIENT_RESOURCES)".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got it.
|
Thanks a lot, really appreciate the fix. |
|
NtQueryObject also sometimes returns STATUS_INFO_LENGTH_MISMATCH if the provided buffer is bigger than expected. At least this the case on my Windows 10 x64 RS4 when using ObjectBasicInformation info class. This code passes a buffer of exactly the required size and gets STATUS_SUCCESS in return: But if I increase the buffer size by 1, the call results in STATUS_INFO_LENGTH_MISMATCH (and no correct value in returnLength): |
ntdll.NtQueryObjectis one in a long list of natives API with a prettyfunny way to use it. It takes a buffer and a buffer size, and on success
fills out the bufffer and write the written length in a
return_lengthparameter.
However if the buffer is not big enough, the return
statusis set toSTATUS_INFO_LENGTH_MISMATCHinciting the developer to embiggen the inputbuffer and re-attempt the call. However
return_lengthparameter is alsosometimes set but it should not be relied upon as long as status is not
equal to
STATUS_SUCCESS.It's use to enumerate Object types in
NtType.LoadTypes. I've added debug log to understand the situation :On a 64-bit build :
On a 32-bit build :
We have a classic buffer overflow since the
return_lengthvalue returned on aSTATUS_INFO_LENGTH_MISMATCHis not quite the correct value (why is that, I honestly don't know or/and care).The fix is inspired by how ProcessHacker use the API :