Comments (8)
I've found solution. I take original pointer from dlsym instead of result from rebind_symbols, and that solves my issue.
void *handle = dlopen("..framework...", RTLD_NOW);
original_SomeFunction = dlsym(handle, "SomeFunction");
if ((rebind_symbols((struct rebinding[1]){{(char *)"SomeFunction", (void *)replaced_SomeFunction}}, 1) < 0))
{
Log(@"Hooking failed.");
}
Call of dlsym provides direct address of function, and that's all.
Conclusion
I based my code on the sample, but looks like it's wrong for some cases. Call of original function could rewrite hook. To avoid that, the simpler solution would be obtained pointer to original function from dlsym, instead of using it from rebind_symbols.
from fishhook.
Did you make your GitHub account just to report this bug? Either way, great username.
I can see why this would happen with lazy binding. Also not sure if it's a bug, but it's definitely confusing. Here's two potential workarounds:
- Load the library with
dlopen(..., RTLD_NOW)
before using fishhook for force immediate symbol binding. - Call the function yourself to force it to be bound first.
Do either of those help for you?
from fishhook.
Did you make your GitHub account just to report this bug? Either way, great username.
ok Thanks, will know.
Do either of those help for you?
Doesn't work.
Option A Simple call dlopen/dlsym/original function
- Force loading framework
dlopen("/Applications/Xcode8.0.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/PrivateFrameworks/WebCore.framework/WebCore", RTLD_NOW); - Obtain pointer to function (dlsym)
- Force call problematic function.
Doesn't work, because dlsym provide real address of function, not a bind stub.
Code is below:
void *handle = dlopen("/Applications/Xcode8.0.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/PrivateFrameworks/WebCore.framework/WebCore", RTLD_NOW);
typedef void * (*SQLiteStatement_bindBlob_func)(void *StorageMapThis, int index, void* WTFStrText);
SQLiteStatement_bindBlob_func func= (SQLiteStatement_bindBlob_func)dlsym(handle, "_ZN7WebCore15SQLiteStatement8bindBlobEiRKN3WTF6StringE");
char *stubData[100] = {0};
func(stubData, 0, stubData);
Call of func looks like this:
0x10000987a <+170>: movq -0x3b0(%rbp), %rax
0x100009881 <+177>: movq -0x3d8(%rbp), %rdi
0x100009888 <+184>: movl -0x3cc(%rbp), %esi
0x10000988e <+190>: movq -0x3d8(%rbp), %rdx
-> 0x100009895 <+197>: callq *%rax
And $rax is original address of function:
(lldb) image lookup -a $rax
Address: WebCore[0x0000000000e12e60] (WebCore.__TEXT.__text + 14749488)
Summary: WebCore`WebCore::SQLiteStatement::bindBlob(int, WTF::String const&)
(lldb)
Code from framework still contains stubs for lazy binding, those operations don't trigger the binding.
Option B More complex way: dlopen/dlsym but call upper function from framework which will call required function. Those must trigger binding mechanism. Unfortunately upper function is not exported, so can't force trigger binding mechanism for required function.
from fishhook.
Sorry, I meant dlopen(..., RTLD_NOW)
on WebCoreLegacy
, which is the one that has the lazy binding.
from fishhook.
Do not really understand.
Lazy binding function will be 'resolved' only after first real call of it. Loading framework which contains it does not resolve issue, because it needs to call original function at least once. Only after that hook can be installed and call of original function from hook will be safety (this will not lead to restoring original function).
In other words, if I postpone installing hook for 10 seconds, than all work as expected. Unfortunately, this is bad case for me, because I lose some important information in that 10 seconds.
I guess, you mean that I should load framework, and this would lead to call original function... but loaded framework doesn't call this function. This functionality linked with browsing webpage inside UIWebview component, and requires interaction from user side. Only in that way is posible to trigger this function.
from fishhook.
My understanding is that RLTD_NOW
immediately binds the lazy symbols, without needing them to be called. From the manpage:
RTLD_NOW All external function references are bound immediately during the call to dlopen().
You can then hook the functions after dlopen()
returns since the symbols should all be bound.
from fishhook.
Usual case of using hook functionality
We have target library which functions have to be hooked.
Also, we have some other code (that could be our App or 3-rd party library) which uses target library functions. And we use fishhook for hooking target library functions.
So, when our App or 3-rd party library is calling target function then our hook is executed. Our hook is calling original function, and at that moment system binds original symbol.
Here is very tricky moment: system code patches/binds lazy symbol inside target library.
#0 0x00000001085ecb8d in ImageLoaderMachO::bindLocation(ImageLoader::LinkContext const&, unsigned long, unsigned long, unsigned char, char const*, long, char const*, char const*, char const*) ()
#1 0x00000001085f197b in ImageLoaderMachOCompressed::doBindFastLazySymbol(unsigned int, ImageLoader::LinkContext const&, void (*)(), void (*)()) ()
#2 0x00000001085dfbb9 in dyld::fastBindLazySymbol(ImageLoader**, unsigned long) ()
#3 0x000000010c358516 in dyld_stub_binder ()
This is fine for us, because lazy symbols inside our App (or 3-rd party library) still contain correct link on our hooked function.
Not obvious case
Now, another case, and it's more interesting (not obvious).
If we hook exported (external) function inside our module. When we are calling from hook function an original function then system does the same work - binds lazy symbol. But this also means that system re-writes our hook pointer. As the result, our hook called only once.
Of course this case looks very strange. Why do we need to hook own function in own module? But bellow is next case which explains my situation.
Now imagine that one function could be present in many frameworks. How it's posible? Well, some frameworks could be based on the same component, as in my case. For example WebCore component (with some functions set) is built with WebKit and also with WebKitLegacy frameworks.
So, in two frameworks is present the same function. When my hook is calling from WebKit and calls original function from WebKitLegacy then this works fine. Becuase system will bind original function inside WebKitLegacy. So, symbol inside WebKit will still has our hook.
But if we are calling hook from WebKitLegacy then our hook also calls original function from this framework, and system rewrites our hook.
In simple words: if we 'install' hook inside module/framework which calls own external function then we will see this bug - our hook will be called only once.
My case
Now imagine that one function could be present in many frameworks. How it's posible? Well, some frameworks could be based on the same component, as in my case. For example WebCore component (with some functions set) is built with WebKit and also with WebKitLegacy frameworks.
So, in two frameworks is present the same function. When my hook is calling from WebKit and calls original function from WebKitLegacy then this works fine. Because system will bind original function inside WebKitLegacy (rewrite our pointer on hook). So, symbol inside WebKit will still have our hook.
But if we are calling hook from WebKitLegacy then our hook also calls original function from this framework, and system rewrites our hook.
In simple words: if we 'install' hook inside module / framework which calls own external function then we will see this bug - our hook will be called only once.
@grp
I do not know why dlopen (RTLD_NOW) does not resolve lazy binding symbols.
Summary
I know the root cause of bug, but still don't know how to fix it in appropriate way. I can again install hook after first call of original function but this requires additional logic in code. Looks like the best way it's reading Apple documentation to understand how to force bind lazy symbols and only after that install hook.
from fishhook.
I wonder if fishhook could improve the situation here checking the GOT entry it's replacing to see if it's dyld_stub_binder
, and if so, replacing it with a thunk that "does the right thing" when called.
from fishhook.
Related Issues (20)
- fishhook crash on iOS 13.0 beta HOT 1
- hook malloc_zone_malloc EXC_BAD_ACCESS (code=2, address=0x10ecb50cd) HOT 1
- Why is there no integration in my project, but running the program will automatically generate Github/fishhookDemo/...
- iOS13 Thread 1: EXC_BAD_ACCESS (code=2, address=0x1287b4390) HOT 3
- Thread 1: EXC_BAD_ACCESS HOT 5
- Why I don't bind CFReadStreamCreateForHTTPRequest, HOT 1
- build fail when __LP64__ is not defined HOT 2
- iOS 13Crash HOT 7
- Hook called in iOS simulator but not called on-device HOT 2
- How thread-safe is fishhook HOT 3
- Add support for installing via Swift Package Manager
- shimmed method doesn't get called from system libraries for iOS14 with arm64e device HOT 4
- iOS 14.5 crashed HOT 13
- iOS 15 crash HOT 8
- test
- Undefined symbol: _rebind_symbols in new commit 'aadc161ac3b80db07a9908851839a17ba63a9eb1' HOT 1
- Crashes with EXC_BAD_ACCESS on Apple Silicon mac when compiled as arm64e HOT 1
- How about adding a Package.swift? HOT 1
- hook C函数open crash HOT 2
- Hooking every hookable targets with independent wrapper fucntion.
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from fishhook.