热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Hooking&ExecutingCodewithdlopen&dlsymCfunctions

Easymode:hookingCfunctionsSettingupyourprojectThisprojectisverysimple.Allitdoesisdisplayaw

Easy mode: hooking C functions

Setting up your project

This project is very simple. All it does is display a watermarked image in a UIImageView.

Easy mode: hooking C functions

you’ll be going after the getenv C function. This simple C function takes a char * (null terminated string) for input and returns the environment variable for the parameter you supply.

Open and launch the Watermark project in Xcode. Create a new symbolic breakpoint, putting getenv in the Symbol section. Next, add a custom action with the following:

po (char *)$rdi

Now, make sure the execution automatically continues after the breakpoint hits.

Finally, build and run the application on the iPhone Simulator then watch the console. You’ll get a slew of output indicating this method is called quite frequently. It’ll look similar to the following:

"DYLD_INSERT_LIBRARIES"
"NSZombiesEnabled"
"OBJC_DEBUG_POOL_ALLOCATION"
"MallocStackLogging"
"MallocStackLoggingNoCompact"
"OBJC_DEBUG_MISSING_POOLS"
"LIBDISPATCH_DEBUG_QUEUE_INVERSIONS"
"LIBDISPATCH_CONTINUATION_ALLOCATOR"
"XPC_FLAGS"
"XPC_NULL_BOOTSTRAP"
"XBS_IS_CHROOTED"
"XPC_SERVICES_UNAVAILABLE"
"XPC_SIMULATOR_LAUNCHD_NAME"
"CLASSIC"
"LANG"
"XPC_SIMULATOR_LAUNCHD_NAME"

you want to have the function hooked before your application starts up, you’ll need to create a dynamic framework to put the hooking logic in so it’ll be available before the main function is called. You’ll explore this easy case of hooking a C function inside your own executable first.

Open AppDelegate.swift, and replace
application(_:didFinishLaunchingWithOptions:) with the following:

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
if let cString = getenv("HOME") {
let homeEnv = String(cString: cString)
print("HOME env: \(homeEnv)")
}
return true
}

This creates a call to getenv to get the HOME environment variable.
Next, remove the symbolic getenv breakpoint you previously created and build and
run the application.
The console output will look similar to the following:

"HOME"
HOME env: /Users/devzkn/Library/Developer/CoreSimulator/Devices/2499648A-9622-42D9-8931-C342DB0208CC/data/Containers/Data/Application/81D1A334-A8C2-498E-87E9-950626126918

In Xcode, navigate to FileNewTarget and select Cocoa Touch Framework. Choose HookingC as the product name, and set the language to Objective-C.

Once this new framework is created, create a new C file. In Xcode, select FileNewFile, then select C file. Name this file getenvhook. Uncheck the checkbox for Also create a header file. Save the file with the rest of the project.

Make sure this file belongs to the HookingC framework that you’ve just created, and not Watermark.

Open getenvhook.c and replace its contents with the following:

import import import import import

/**
• dlfcn.h will be responsible for two very interesting functions: dlopen and dlsym.
• assert.h the assert will test the library containing the real getenv is correctly loaded.
• stdio.h will be used temporarily for a C printf call.
• dispatch.h will be used to to properly set up the logic for GCD’s dispatch_once
function.
• string.h will be used for the strcmp function, which compares two C strings.
*/

Next, redeclare the getenv function with the hard-coded stub shown below:

char * getenv(const char *name) {
return "YAY!";
}

Finally, build and run your application to see what happens. You’ll get the following output:
HOME env: YAY!

HOME env: YAY!
"IPHONE_SIMULATOR_ROOT"
IPHONE_SIMULATOR_ROOT: YAY!

Awesome! You were able to successfully replace this method with your own function.

However, this isn’t quite what you want. You want to call the original getenv function and augment the return value if “HOME” is supplied as input.

What would happen if you tried to call the original getenv function inside your getenv function? Try it out and see what happens. Add some temporary code so the getenv looks like the following:

char * getenv(const char *name) {
return getenv(name);
return "YAY!";
}

如果要在Terminal进行lldb的话,就关闭Xcode的运行,使用模拟器单独启动Watermark

如果要在Xcode进行lldb 的话,可以设置个断点即可

devzkndeMacBook-Pro:~ devzkn$ lldb -n Watermark

image lookup -s getenv
1 symbols match 'getenv' in /Users/devzkn/Library/Developer/Xcode/DerivedData/Watermark-eqvbswmyqiyhvgbqmiqzpnnzylsn/Build/Products/Debug-iphonesimulator/Watermark.app/Frameworks/HookingC.framework/HookingC:
Address: HookingC[0x0000000000000f60] (HookingC.__TEXT.__text + 0)
Summary: HookingC`getenv at getenvhook.c:29
1 symbols match 'getenv' in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//usr/lib/system/libsystem_c.dylib:
Address: libsystem_c.dylib[0x000000000005ca26] (libsystem_c.dylib.__TEXT.__text + 375174)
Summary: libsystem_c.dylib`getenv

You’ll get two hits. One of them will be the getenv function you created yourself. More importantly, you’ll get the location of the getenv function you actually care about. It looks like this function is located in libsystem_c.dylib, and its full path is at /usr/lib/system/libsystem_c.dylib. Remember, the simulator prepends that big long path to these directories, but the dynamic linker is smart enough to search in the correct areas. Everything after iPhoneSimulator.sdk is where this framework is actually stored on a real iOS device.

Now you know exactly where this function is loaded, it’s time to whip out the first of the amazing “dl” duo, dlopen. Its function signature looks like the following:

extern void * dlopen(const char * __path, int __mode);
extern void * dlsym(void * __handle, const char * __symbol);```
dlopen expects a fullpath in the form of a char * and a second parameter, which is a mode expressed as an integer that determines how dlopen should load the module. If successful, dlopen returns an opaque handle (a void *) ,or NULL if it fails.
After dlopen (hopefully) returns a reference to the module, you’ll use dlsym to get a reference to the getenv function. dlsym has the following function signature:
extern void * dlsym(void * __handle, const char * __symbol);
dlsym expects to take the reference generated by dlopen as the first parameter and the name of the function as the second parameter. If everything goes well, dlsym will return the function address for the symbol specified in the second parameter or NULL if it failed.
Replace your getenv function with the following:

char getenv(const char name) {
void *handle = dlopen(“/usr/lib/system/libsystem_c.dylib”, RTLD_NOW);
assert(handle);
void *real_getenv = dlsym(handle, “getenv”);
printf(“Real getenv: %pnFake getenv: %pn”, real_getenv, getenv);
return “YAY!”;
}

You used the RTLD_NOW mode of dlopen to say, “Hey, don’t wait or do any cute lazy loading stuff. Open this module right now.” After making sure the handle is not NULL through a C assert, you call dlsym to get a handle on the “real” getenv.
Build and run the application. You’ll get output similar to the following:

Real getenv: 0x10fde4a26
Fake getenv: 0x10d081df0


Right now, the real_getenv function pointer is void *, meaning it could be anything. You already know the function signature of getenv, so you can simply cast it to that.
Replace your getenv function one last time with the following:

char * getenv(const char *name) {
static void *handle; // 1
static char * (*real_getenv)(const char *); // 2
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ // 3
handle = dlopen("/usr/lib/system/libsystem_c.dylib", RTLD_NOW);
assert(handle);
real_getenv = dlsym(handle, "getenv");
});
if (strcmp(name, "HOME") == 0) { // 4
return "/"; }
return real_getenv(name); // 5
}

You might not be used to this amount of C code, so let’s break it down:

  1. Thiscreatesastaticvariablenamedhandle.It’sstaticsothisvariablewill survive the scope of the function. That is, this variable will not be erased when the function exits, but you’ll only be able to access it inside the getenv function.
  2. You’re doing the same thing here as you declare the real_getenv variableas static, but you’ve made other changes to the real_getenv function pointer. You’ve cast this function pointer to correctly match the signature of getenv. This will allow you to call the real getenv function through the real_getenv variable. Cool, right?
  3. You’re using GCD’s dispatch_once because you really only need to call the setup once. This nicely complements the static variables you declared a couple lines

    1. You don’t want to be doing the lookup logic every time your augmented
  4. runs!
  5. You’re using C’s strcmp to see ifyou’re querying the “HOME” environment variable. If it’s true, you’re simply returning “/” to signify the root directory. Essentially, you’re overriding what the getenv function returns.
  6. If “HOME” is not supplied as an input parameter,then just fallback on the default getenv.

Find application(_:didFinishLaunchingWithOptions:) in AppDelegate.swift. Replace this method with:

func application(_ application: UIApplication,

didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
if let cString = getenv("HOME") {
let homeEnv = String(cString: cString)
print("HOME env: \(homeEnv)")
} if let cString = getenv("IPHONE_SIMULATOR_ROOT") {
let homeEnv = String(cString: cString)
print(" IPHONE_SIMULATOR_ROOT: \(homeEnv)")
}
/**

HOME env: /Users/devzkn/Library/Developer/CoreSimulator/Devices/2499648A-9622-42D9-8931-C342DB0208CC/data/Containers/Data/Application/E0628F2F-2491-4F86-A7D2-3C3375526D60
"IPHONE_SIMULATOR_ROOT"

IPHONE_SIMULATOR_ROOT: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
*/ return true
}

HOME env: /
IPHONE_SIMULATOR_ROOT: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk

推荐阅读
  • 前言--页数多了以后需要指定到某一页(只做了功能,样式没有细调)html ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 本文探讨了如何在给定整数N的情况下,找到两个不同的整数a和b,使得它们的和最大,并且满足特定的数学条件。 ... [详细]
  • 本文详细探讨了KMP算法中next数组的构建及其应用,重点分析了未改良和改良后的next数组在字符串匹配中的作用。通过具体实例和代码实现,帮助读者更好地理解KMP算法的核心原理。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • Java 中 Writer flush()方法,示例 ... [详细]
  • 本文探讨了Hive中内部表和外部表的区别及其在HDFS上的路径映射,详细解释了两者的创建、加载及删除操作,并提供了查看表详细信息的方法。通过对比这两种表类型,帮助读者理解如何更好地管理和保护数据。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 题目描述:给定n个半开区间[a, b),要求使用两个互不重叠的记录器,求最多可以记录多少个区间。解决方案采用贪心算法,通过排序和遍历实现最优解。 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 本文详细解析了Python中的os和sys模块,介绍了它们的功能、常用方法及其在实际编程中的应用。 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • Windows服务与数据库交互问题解析
    本文探讨了在Windows 10(64位)环境下开发的Windows服务,旨在定期向本地MS SQL Server (v.11)插入记录。尽管服务已成功安装并运行,但记录并未正确插入。我们将详细分析可能的原因及解决方案。 ... [详细]
  • 本文详细探讨了Java中的24种设计模式及其应用,并介绍了七大面向对象设计原则。通过创建型、结构型和行为型模式的分类,帮助开发者更好地理解和应用这些模式,提升代码质量和可维护性。 ... [详细]
author-avatar
nikechen
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有