44
55#include " SharedCacheUINotifications.h"
66#include < sharedcacheapi.h>
7+ #include " mediumlevelilinstruction.h"
78#include " ui/sidebar.h"
89#include " ui/linearview.h"
910#include " ui/viewframe.h"
@@ -15,6 +16,57 @@ using namespace SharedCacheAPI;
1516
1617UINotifications* UINotifications::m_instance = nullptr ;
1718
19+ // Resolve a stub function at `addr` to the constant target of its jump or tail call. Stub functions
20+ // are identified by the `j_` symbol prefix that the shared cache workflow applies when renaming them.
21+ static std::optional<uint64_t > ResolveStubTarget (BinaryView& view, uint64_t addr)
22+ {
23+ auto symbol = view.GetSymbolByAddress (addr);
24+ if (!symbol || symbol->GetShortName ().rfind (" j_" , 0 ) != 0 )
25+ return std::nullopt ;
26+
27+ auto func = view.GetAnalysisFunction (view.GetDefaultPlatform (), addr);
28+ if (!func)
29+ return std::nullopt ;
30+
31+ // Skip any function that is very clearly not a stub (not a single basic block with only a few instructions).
32+ const auto blocks = func->GetBasicBlocks ();
33+ constexpr uint64_t maxStubLength = 0x20 ;
34+ if (blocks.size () != 1 || blocks[0 ]->GetLength () > maxStubLength)
35+ return std::nullopt ;
36+
37+ auto mlil = func->GetMediumLevelIL ();
38+ if (!mlil)
39+ return std::nullopt ;
40+
41+ const auto mlilBlocks = mlil->GetBasicBlocks ();
42+ if (mlilBlocks.size () != 1 || mlilBlocks[0 ]->GetEnd () == mlilBlocks[0 ]->GetStart ())
43+ return std::nullopt ;
44+
45+ // The jump or tail call terminates the stub's single basic block, so it can only be the last instruction.
46+ const auto instr = mlil->GetInstruction (mlilBlocks[0 ]->GetEnd () - 1 );
47+ if (instr.operation != MLIL_JUMP && instr.operation != MLIL_TAILCALL )
48+ return std::nullopt ;
49+
50+ const auto dest = instr.GetDestExpr ();
51+ if (dest.operation != MLIL_CONST_PTR && dest.operation != MLIL_CONST )
52+ return std::nullopt ;
53+
54+ return dest.GetConstant ();
55+ }
56+
57+ // The address a token-based load action should operate on. A stub function's address is in an
58+ // already-loaded image, so resolve it to its target and offer to load what the stub jumps to.
59+ static uint64_t TokenAddress (const UIActionContext& ctx)
60+ {
61+ uint64_t addr = ctx.token .token .value ;
62+ if (!ctx.binaryView ->GetSectionsAt (addr).empty ())
63+ {
64+ if (auto target = ResolveStubTarget (*ctx.binaryView , addr))
65+ return *target;
66+ }
67+ return addr;
68+ }
69+
1870void UINotifications::init ()
1971{
2072 m_instance = new UINotifications;
@@ -95,19 +147,21 @@ void UINotifications::OnViewChange(UIContext* context, ViewFrame* frame, const Q
95147 };
96148
97149 auto loadRegionTokenAction = [](const UIActionContext& ctx) {
150+ uint64_t addr = TokenAddress (ctx);
98151 BackgroundThread::create (ctx.context ->mainWindow ())
99- ->thenBackground ([ctx](){ loadRegionAtAddr (*ctx.binaryView , ctx. token . token . value ); })
152+ ->thenBackground ([ctx, addr ](){ loadRegionAtAddr (*ctx.binaryView , addr ); })
100153 ->start ();
101154 };
102155
103156 auto loadImageTokenAction = [](const UIActionContext& ctx) {
157+ uint64_t addr = TokenAddress (ctx);
104158 BackgroundThread::create (ctx.context ->mainWindow ())
105- ->thenBackground ([ctx](){ loadImageAtAddr (*ctx.binaryView , ctx. token . token . value ); })
159+ ->thenBackground ([ctx, addr ](){ loadImageAtAddr (*ctx.binaryView , addr ); })
106160 ->start ();
107161 };
108162
109163 auto isValidUnloadedRegionAction = [](const UIActionContext& ctx) {
110- uint64_t addr = ctx. token . token . value ;
164+ uint64_t addr = TokenAddress ( ctx) ;
111165 // Check if the region is already loaded in the view.
112166 if (!ctx.binaryView ->GetSectionsAt (addr).empty ())
113167 return false ;
@@ -118,7 +172,7 @@ void UINotifications::OnViewChange(UIContext* context, ViewFrame* frame, const Q
118172 };
119173
120174 auto isValidUnloadedImageAction = [](const UIActionContext& ctx) {
121- uint64_t addr = ctx. token . token . value ;
175+ uint64_t addr = TokenAddress ( ctx) ;
122176 // Check if the image is already loaded in the view.
123177 if (!ctx.binaryView ->GetSectionsAt (addr).empty ())
124178 return false ;
@@ -139,7 +193,7 @@ void UINotifications::OnViewChange(UIContext* context, ViewFrame* frame, const Q
139193 auto controller = SharedCacheController::GetController (*ctx.binaryView );
140194 if (!controller)
141195 return QString (" NO CONTROLLER" );
142- uint64_t addr = ctx. token . token . value ;
196+ uint64_t addr = TokenAddress ( ctx) ;
143197 auto region = controller->GetRegionContaining (addr);
144198 if (!region)
145199 return QString (" NO REGION" );
@@ -150,7 +204,7 @@ void UINotifications::OnViewChange(UIContext* context, ViewFrame* frame, const Q
150204 auto controller = SharedCacheController::GetController (*ctx.binaryView );
151205 if (!controller)
152206 return QString (" NO CONTROLLER" );
153- uint64_t addr = ctx. token . token . value ;
207+ uint64_t addr = TokenAddress ( ctx) ;
154208 auto image = controller->GetImageContaining (addr);
155209 if (!image)
156210 return QString (" NO IMAGE" );
0 commit comments