Skip to content
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

Prolog sanity check failed - multiple inheritance offsets #275

Closed
Detectronic-PB opened this issue Nov 26, 2024 · 25 comments
Closed

Prolog sanity check failed - multiple inheritance offsets #275

Detectronic-PB opened this issue Nov 26, 2024 · 25 comments
Assignees

Comments

@Detectronic-PB
Copy link

I'm getting this error trying to run OOProlog on a Kensington firmware update utility:

Consistency checks failed.
Class 0x587970 inherits from 0x569a4c at offsets 0 and 0x244
Initial sanity check failed, indicating the OO rules are incorrect.

There are several warnings/errors at the OOAnalyzer stage which may be related.


Container Version: seipharos/pharos:latest (sha256:fe09ad8e492115b7a1cfe0899995fa37057089d695d73332aa633b7f696f33bd)

Input file: KensingtonUpdate.exe

API database JSON files: ApiJson.zip

Logs: Logs.zip

Partition command:

partition
    --serialize=kensington/kensington-sem.ser
    --maximum-memory=10240
    kensington/KensingtonUpdate.exe

Analyzer command:

ooanalyzer
    --serialize=kensington/kensington-sem.ser
    --new-method=0x4116a6
    --new-method=0x4116d8
    --new-method=0x42f692
    --delete-method=0x42f6ae
    --delete-method=0x53b4c0
    --maximum-memory 18000
    --per-function-maximum-memory=0
    --prolog-facts=kensington/kensington-facts.pl
    --threads=1
    --per-function-timeout=600
    --apidb=/usr/local/share/pharos/contrib/winspool.json
    --apidb=kensington/hid.xml.json
    --apidb=kensington/gdiplus.xml.json
    --apidb=kensington/SetupAPI.xml.json
    --apidb=kensington/UxTheme.xml.json
    --apidb=kensington/Imm32.xml.json
    --apidb=kensington/Msimg32.xml.json
    --apidb=kensington/Oleacc.xml.json
    --apidb=kensington/User32.xml.json
    --apidb=kensington/OleDlg.xml.json
    kensington/KensingtonUpdate.exe

Note: --threads=1 is to avoid a multithread issue that I'll submit/add to another issue for.

Prolog command:

ooprolog
    --facts kensington/kensington-facts.pl
    --results kensington/kensington-results.pl
    --log-level=6
@Detectronic-PB
Copy link
Author

Forgot the facts:
Facts.zip

@sei-eschwartz
Copy link
Collaborator

Thanks, I will take a look at this. The threading problem is probably #267.

@sei-eschwartz
Copy link
Collaborator

Working backwards...

Consistency checks failed.
Class 0x587970 inherits from 0x569a4c at offsets 0 and 0x244
Merging class 0x50b8db into 0x587970 ...
reasonMergeVFTables_A(constructor, 0x587970, 0x50b8db, 0x587970, 0, factVFTableWrite(0x50b8f8, 0x50b8db, 0, 0x587970)).

@sei-eschwartz
Copy link
Collaborator

[eschwartz@pd4 ooanalyzer-tests]$ /home/mwd/git/plogtrace/plogtrace.py ./code/testcases/kensington/kensington.exe.results.log 'factObjectInObject(0x587970, 0x569a4c, 0x244)'
1487178: Retracting factObjectInObject(0x50b8db, 0x569a4c, 0x244) and asserting factObjectInObject(0x587970, 0x569a4c, 0x244) ...
1487060: Merging class 0x50b8db into 0x587970 ...
895020: Retracting factObjectInObject(0x50b8db, 0x426738, 0x244) and asserting factObjectInObject(0x50b8db, 0x569a4c, 0x244) ...
894951: Merging class 0x426738 into 0x569a4c ...
152343: Concluding factObjectInObject(0x50b8db, 0x426738, 0x244).
reasonObjectInObject_D(0x50b8db, 0x426738, 0x244, validMethodCallAtOffset(0x50b92d, 0x50b8db, 0x426738, 0x244)).
% This rule is a special case of the reasonObjectInObject_E, that relies on the fact that it
% does not matter whether the InnerClass and OuterClass are provably different or whether
% they're just currently not assigned to the same class.  The key observation is that the
% distinction only matters when offset is non-zero, because that fact alone rules out the
% possibility that the two classes are in fact the same class.

@sei-eschwartz
Copy link
Collaborator

0x587970 is the vftable for .?AVCMFCPropertyGridCtrl@@
and 0x569a4c is the vftable for .?AVCWnd@@

@sei-eschwartz
Copy link
Collaborator

According to this:

class CMFCPropertyGridCtrl : public CWnd

So what is going on with the offset at 0x244?

@Detectronic-PB
Copy link
Author

Just to add some more information in case it's relevant.

I identified 3 new operators and 2 delete operators, in a potentially strange configuration:

  • 0x004116a6 is the main new operator
  • 0x0053b4c0 is the main delete operator
  • 0x004116d8 is a wrapper around the main new operator
  • 0x0042f692 is CNoTrackObject::operator_new
  • 0x0042f6ae is CNoTrackObject::operator_delete

Having double checked these just now I've found a wrapper for the main delete operator as well, I will try running again with that one defined.

@sei-eschwartz
Copy link
Collaborator

At 0x50b92d of 0x50b8db (probably MFCPropertyGridCtrl's constructor), we call 0x426738 at offset 0x244.

@sei-eschwartz
Copy link
Collaborator

sub_426738 proc near
push    esi
mov     esi, ecx
call    sub_42305A
xor     eax, eax
mov     dword ptr [esi], offset ??_7CWnd@@6B@ ; const CWnd::`vftable'
mov     dword ptr [esi+30h], offset ??_7XAccessible@CWnd@@6B@ ; const CWnd::XAccessible::`vftable'
mov     dword ptr [esi+34h], offset ??_7XAccessibleServer@CWnd@@6B@ ; const CWnd::XAccessibleServer::`vftable'
mov     [esi+3Ch], eax
mov     [esi+40h], eax
or      dword ptr [esi+3Ch], 0FFFFFFFFh
or      dword ptr [esi+40h], 0FFFFFFFFh
mov     [esi+20h], eax
mov     [esi+24h], al
mov     [esi+38h], eax
mov     [esi+2Ch], eax
mov     [esi+28h], eax
mov     [esi+54h], eax
mov     [esi+58h], eax
mov     [esi+5Ch], eax
mov     [esi+60h], eax
mov     [esi+64h], eax
mov     [esi+68h], eax
mov     [esi+6Ch], eax
mov     [esi+70h], eax
mov     [esi+44h], eax
mov     [esi+48h], eax
mov     [esi+4Ch], eax
mov     [esi+50h], eax
mov     eax, esi
pop     esi
retn
sub_426738 endp

@sei-eschwartz
Copy link
Collaborator

According to OOAnalyzer, 0x426738 is CWnd::CWnd.

@sei-eschwartz
Copy link
Collaborator

I think the MFC classes in this program are triggered a rare-but-not-unknown problem in a sanity rule, insanityInheritTwice. Comment out this line and try to run again.

@Detectronic-PB
Copy link
Author

Initial results are promising, it's on to the guess phase.

As an aside, are any of the errors in the analysis log concerning? I'm assuming it will just result in slightly less information extracted than otherwise.

@sei-eschwartz
Copy link
Collaborator

Nothing too concerning, especially for a program of that size

@Detectronic-PB
Copy link
Author

Detectronic-PB commented Nov 29, 2024

After a few adjustments to memory and swap on the container I'm now getting:

reasoningLoop: post-reason sanityChecks
failed.
Consistency checks failed.
Contradictory information about constructor: factConstructor(0x426738) but reasonNOTConstructor(0x426738)
Constraint checks failed, retracting guess!
....
Fail-Retracting factNOTConstructor(0x4f7de3)...
setting numGroup to 0x2 failed so setting to 1
Fail-Retracting guessedNOTConstructor(0x52b9d4)...
Fail-Retracting factNOTConstructor(0x52b9d4)...
tryBinarySearch completely failed on [0x52b9d4] and will now backtrack to fix an upstream problem.
guess: We have back-tracked to the call of tryBinarySearch(tryConstructor, tryNOTConstructor, [0x52b9d4, 0x4c96e7, 0x424cde, 0x415931, 0x536cb0, 0x5357ff, 0x52d483, 0x52d050, 0x52867f, 0x52856d, 0x527ea9, 0x527ae7, 0x521244, 0x5142c3, 0x50813f, 0x502989, 0x4f8465, 0x4dcf68, 0x4d193e, 0x4cfe49, 0x4ccd22, 0x4ca61a, 0x4bfb3e, 0x4aa46f, 0x4a9fc1, 0x4a7719, 0x4a0e6c, 0x499ec9, 0x471a5b, 0x45786b, 0x43cf62, 0x43a0a2, 0x4302d3, 0x425e15, 0x425d6d, 0x4153f0, 0x41467c, 0x4133ec, 0x4133c5])
Refusing to backtrack into reasoningLoop to fix an upstream problem because backtrackForUpstream/0 is not set.

Full log: prolog.zip

@sei-eschwartz
Copy link
Collaborator

This is pretty unusual. Most times it happens it is a problem with fact generation. I'll try to look on Monday.

@edmcman
Copy link
Contributor

edmcman commented Dec 2, 2024

From a quick glance, it does look like a constructor to me.

@edmcman
Copy link
Contributor

edmcman commented Dec 2, 2024

Guessing factConstructor(0x52b9d4).
Contradictory information about constructor: factConstructor(0x426738) but reasonNOTConstructor(0x426738)
Guessing factNOTConstructor(0x52b9d4).
Contradictory information about constructor: factConstructor(0x426738) but reasonNOTConstructor(0x426738)

So no matter what guess we make about 0x52b9d4, we decide that 0x426738 is both a constructor and not a constructor.

0x52b9d4 is probably CScrollView::CScrollView. 0x426738 is probably CWnd::CWnd. 0x52b9d4 calls 0x4f7de3, which calls 0x426738.

reasonNOTConstructor_F(0x426738, 0x4f7de3) is why we are concluding about 0x426738... 0x4f7de3 is the intermediate function above.

NOTConstructor_F is:

% Because it is called by a non-constructor on the same object instance.

So that makes sense if we guess that 0x52b9d4 is not a constructor. What about when we guess it is a constructor?

reasonNOTConstructor_G(0x52b9d4, 0x4f7de3)

So there we decide that 0x4f7de3 is not a constructor because of _G, which is:

% Because you can't be a constructor on a class that's already known to have a VFTable if you
% you don't have a VFTable. This rule was added largely to reach the correct conclusions on
% methods like operator=.

Interesting.

@edmcman
Copy link
Contributor

edmcman commented Dec 2, 2024

So, the puzzle here is What the heck is 0x4f7de3?

@sei-eschwartz
Copy link
Collaborator

Oops, back to work persona. I think I figured out what is going wrong with the help of https://github.com/edmcman/MFCApplication.

This confirms that CScrollView::CScrollView calls CView::View:

image

Now, surprisingly to me, CView::CView does not have a vftable installation:

image

This is at odds with reasonNOTConstructor_G. Then after a little digging, I found that interface classes in ATL like CView use a special property called novtable.

@sei-eschwartz
Copy link
Collaborator

What should we do about this? In general, I'm not sure. If I were you for the moment, I would hack reasonNOTConstructor_G so that it ignores the vftable for CScrollView.

% Because you can't be a constructor on a class that's already known to have a VFTable if you
% you don't have a VFTable.  This rule was added largely to reach the correct conclusions on
% methods like operator=.
% ED_PAPER_INTERESTING
reasonNOTConstructor_G(Method) :-
    % There's another method that calls this method on the same object pointer.
    validMethodCallAtOffset(_, Caller, Method, 0),
    % The caller is known to be a constructor or destructor.
    (factConstructor(Caller); factRealDestructor(Caller)),
    % The caller is already known to have a VFTable write.
    factVFTableWrite(_Insn1, Caller, 0, VFTable1),
    % HACK: NOT CScrollView::vftable
    iso_dif(VFTable1, 0x58eb84),
    % But this method doesn't have the required write.
    not(possibleVFTableWrite(_Insn2, Method, _ThisPtr, 0, _VFTable2)),
    % Since we don't have visibility into VFTable writes from imported constructors and
    % destructors this rule does not apply to imported methods.
    not(symbolClass(Method, _, _, _)),
    % Debugging
    logtraceln('~@~Q.', [not(factNOTConstructor(Method)),
                         reasonNOTConstructor_G(Caller, Method)]).

@Detectronic-PB
Copy link
Author

Judging by the MSDN it seems that 0x4f7de3 is CView::CView.

This leads to the documented inheritance of CScrollView : CView : CWnd.

So _G seems to be at issue. What does "if you don't have a VFTable" actually mean in this context?

@Detectronic-PB
Copy link
Author

Oops, the window hadn't updated. I'll do that hack and see if I can make progress.

@Detectronic-PB
Copy link
Author

After a couple of false starts and a switch to a machine with more memory, it finally completed. There was one more mod needed to avoid tripping up reasonNOTConstructor_G again.

@sei-eschwartz
Copy link
Collaborator

Glad to hear it! I'm going to close this since we created #276 to keep track of the more general problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants