-
Notifications
You must be signed in to change notification settings - Fork 176
Linker issue when indirect branch obfuscation is active #34
Comments
Can you reproduce this problem when building for iOS/MacOS? I don't have Android Studio installed. |
Probably COMDAT issue, just remove all comdats |
You don't need Android Studio. Just install NDK for you host machine from https://developer.android.com/ndk/downloads
After that invoke the linker via NDK's clang
This can help to reproduce the issue quickly. |
MachO has no such concept as COMDAT, so this bug will never trigger on Darwin |
Thank you for your reply, I don't know anything about ELF files, I need some time to understand it and try to fix it. |
As mentioned, just remove all the COMDATs should fix the issue |
@NeHyci I did some tests to give you details to understand how to fix it. Let's try to reproduce it as simple as possible. header.hpp
foo.cpp bar.cpp The linker will give such output:
Let's generate human readable bitcode for both cpp files for normal situation (without indirect branch obfuscation).
bar.ll.ok:
As we can see, there are no COMDAT But the obfuscated by the indirect branch bitcode has COMDAT!!!
foo.ll.bad
@Naville How to deal with COMDAT here? When we use indirect branch obfuscation, both foo.ll.bad and bar.ll.bad have definition of the template function from the header.hpp inside and it is marked as comdat. If we will remove the COMDAT mark from the template function definition, we will get duplicated definitions and linking also fails. If we completely remove |
@Naville In my understanding, the root of the problem are the header files from STL which has a lot of definitions of small (2-3 lines) template functions which are normally always inlined. Probably, I didn't get your idea how to manage this issue... |
@Naville I guess, you meant that we have to do each function as local to the specific compilation unit. So it will be kind of the static function (from the C perspective) Could you, please, confirm that? And another one question. |
@NeHyci I edited manually both *bad.ll files with removing After that, I was able to link the bitcode modules without issues! |
I doubt changing linkage is required |
The main question for me is why inlining transformation won't work when the indirect branching is active. |
You can |
Because your current pass pipeline is stupid, you need to think about where your pass fits the best in the pipeline |
@NeHyci I think the best solution for this issue would be pipeline management so that to run indirect branch obfuscation AFTER the inliner. |
@NewDwarf I'm still trying to build NDK....It was very slow |
If you need any help to assist to build exactly the same version of clang as NDK has, just ping me. |
But how do we ensure that the Pass Plugin executes after the inliner and it works for all pipelines? |
It's simple. As @Naville mentioned, we have to use the hidden parameter -debug-pass=Executions |
I mean if we use Obfuscation Pass as the LLVM Pass Plugin, how can we make it execute after AlwaysInliner in all pipelines? I've taken a cursory look at the source code in lib/Passes and this seems impossible. |
Injection order is execution order for Transform passes |
Statistic output produced by the clang/opt
The order is in llvm//lib/Transforms/Scalar/PartiallyInlineLibCalls.cpp
|
This is not the pass you are looking for? This is for library calls |
Forgot to mention, I have been already working with such code
|
Honestly don't understand your reasoning here anymore, we'll review the actual full patch when submit it |
I am new in compilers. |
No worries, we will review your patch together. It's just information lost during our discussion here |
@Naville Could you, please, give a clue why I don't see "Enable Obfuscation" statistic in the The naive approach is just add as the dependency "PartiallyInlineLibCallsLegacyPass" in the obfuscator pass registration
but the main question here - is PartiallyInlineLibCallsLegacyPass always passed to the pipeline? |
The output is the passname, which you change by overriding "StringRef getPassName() const" in LPM
Wrong approach,wrong pass |
I noticed that optimization level 0 (-O0) also inserts COMDAT's regardless of using the indirect branch obfuscation. Using the same code
The bitcode with -0O and DISABLED indirect branch obfuscation looks
and bar.ll
In contrast, the code with ENABLED indirect branch obfuscation
and bar.ll.bad
|
If we remove any references to the
|
Again, comdat issue |
|
@Naville Quick question to you.
If I correctly understood, this is because of opaque pointers. |
Disable opaque pointer mode |
The problem here is who marks the functions as comdat. |
@Naville Thanks a lot for assistance. Removing comdats directly before running the indirect branch obfuscation helped! |
@NeHyci The patch is very simple
|
@Naville But, anyway, it would be nice to understand original issue as the linker is able to resolve correctly comdats of
when I pass
|
Wrong, if you use clang, you instruct clang to emit opaque pointer IR (or not), that -mllvm only configures the LLVM level option, CFE still emits IR in default type system |
NDK's clang doesn't accept the parameter -Xclang -no-opaque-pointers. |
But it doesn't work for NDK STL. It is definitely necessary to look at the "PartiallyInlineLibCallsLegacyPass" direction and run the indirect branch obfuscator only when "PartiallyInlineLibCallsLegacyPass" finish its work. |
What on earth makes you think PartiallyInlineLibCallsLegacyPass is related, I'm curious |
Try the opposite, branchtable->setComdat(Func.getComdat()) |
@Naville I had some more changes which filtered some symbols in the indirect branch pass. I removed them and checked again |
@Naville Are there any clang/opt options to check LLVM transformation after the specific pass? |
|
@NeHyci Did you have a chance to reproduce this issue? |
I think this obfuscator is designed primarily for iOS/MacOS, so issues for other platforms are a low priority. And NDK takes a lot of time to build, I don't have a lot of free time. |
It takes ~1 hour to build from the scratch on my MacBook pro 15'' 2019. Each time, when you update the obfuscation specific codebase, recompiling takes about 1-2 minutes.
|
您好,能否指导一下如何在ndk25中使用这个混淆? |
我编译完后将clang、clang++、clang-format替换进ndk25中,另外补齐了缺失的.h,但用Android studio编译的时候还是会出错,请问我的方法正确吗? |
NDK官方的整套链路是一坨狗屎, 你需要照着原版的clang设置自己编译的那份的LLVM的三个版本号, 然后只替换几个关键文件. EDIT: Replied in Telegram |
It's strange that before the commit of fix pass pipelines, I integrated it into NDK and used it normally. However, after that, I also encountered such issue |
I faced with the interesting issue specific to the indirect branch obfuscation in c++ code.
The linker reports
The data references here are the offset to the IndirectBranchingTargetAddress table.
The issue can be reproduced using only the -mllvm -enable-indibran parameter.
Here is the sample files first.cpp and second.cpp used to create the shared library.
first.cpp
second.cpp
Compile them
-mllvm -enable-bcfobf -mllvm -enable-splitobf -mllvm -split_num=9 are used to emit more code with the branches so that to give the chance to the indirect branch obfuscator to construct the IndirectBranchingTargetAddress table.
Finally, link the object files
clang++ -target aarch64-none-linux-android21 -fPIC -shared -Wl,-soname,libsample.so -o libsample.so first.o second.o
to reproduce the issue.
The text was updated successfully, but these errors were encountered: