LLDB查询对象存储区域(mach-o 栈区 堆区)
#import "LGCatAddress.h"
// 1. C++插件
// 2. 模版
// 3. lldb + 基本上所有mach-o,执行其他代码信息
// cat address 0x00010000010
// 命令传递的参数
namespace LG {
using lldb::eReturnStatusFailed;
using lldb::eReturnStatusSuccessFinishResult;
using lldb::SBCommandInterpreter;
using lldb::SBCommandReturnObject;
using lldb::SBDebugger;
using lldb::SBError;
using lldb::SBExpressionOptions;
using lldb::SBFrame;
using lldb::SBStream;
using lldb::SBSymbol;
using lldb::SBTarget;
using lldb::SBThread;
using lldb::SBValue;
using lldb::SBAddress;
using lldb::SBSection;
using lldb::SBModule;
std::tuple tryMachOAddress(SBAddress addr, SBTarget target) {
// section
SBSection section = addr.GetSection();
if (!section.IsValid()) {
return std::make_tuple(false, nullptr);
}
NSMutableString *sectionName = [NSString stringWithUTF8String:section.GetName()].mutableCopy;
SBSection tmpS = section;
while (tmpS.GetParent().IsValid()) {
tmpS = tmpS.GetParent();
sectionName = [NSMutableString stringWithFormat:@"%s.%@", tmpS.GetName(), sectionName];
}
SBModule module = addr.GetModule();
if (module.IsValid()) {
sectionName = [NSMutableString stringWithFormat:@"%s.%@", addr.GetModule().GetFileSpec().GetFilename(), sectionName];
}
uint64_t addrOffset = addr.GetLoadAddress(target) - section.GetLoadAddress(target);
[sectionName appendFormat:@" +%llx", addrOffset];
SBSymbol symbol = addr.GetSymbol();
NSMutableString *returnDescription = [NSMutableString stringWithFormat:@"%llx", addr.GetOffset()];
if (symbol.IsValid()) {
[returnDescription appendFormat:@"%s", symbol.GetName()];
SBAddress startAddr = symbol.GetStartAddress();
uint64_t addrOffset = addr.GetLoadAddress(target) - startAddr.GetLoadAddress(target);
[returnDescription appendFormat:@" <+%llu> ", addrOffset];
if (symbol.GetMangledName()) {
[returnDescription appendFormat:@", (%s)", symbol.GetMangledName()];
}
[returnDescription appendFormat:@", External: %@ ", symbol.IsSynthetic() ? @"YES" : @"NO"];
}
[returnDescription appendFormat:@"%@", sectionName];
return std::make_tuple(true, returnDescription.UTF8String);
}
std::tuple tryStackAddress(SBAddress addr, SBTarget target) {
// frame -> thread
SBThread thread = target.GetProcess().GetSelectedThread();
uint32_t num_frames = thread.GetNumFrames();
SBStream stream;
for (UInt32 i = 0; i < num_frames; i++) {
const uint64_t address = addr.GetLoadAddress(target);
SBFrame frame = thread.GetFrameAtIndex(i);
const uint64_t fp = frame.GetFP();
const uint64_t sp = frame.GetSP();
if (address >= sp && address <= fp) {
stream.Printf("stack address (SP: 0x%llx FP: 0x%llx) %s", sp, fp, frame.GetFunctionName());
return std::make_tuple(true, [NSString stringWithUTF8String:stream.GetData()].UTF8String);
}
}
return std::make_tuple(false, nullptr);
}
// 插件执行的环境
// 执行插件所在的调试环境
// lldb -》 运行在一个进程上
// exec -〉 运行在一个进程上 lldb -》debugserver
// lldb -> 加载这个插件的-》调代码-〉cat address
// 2个环境 -》 porcess -〉 2堆
// 开发porcess
//hmap
// 执行插件-》调试环境-〉执行相关的判断代码
// 自定义协议 + 客户端 服务端 大厂用 人力物力成本大 引擎
// 动态库 + 苹果点头
std::tuple tryHeapAddress(SBAddress addr, SBTarget target) {
SBStream stream = SBStream();
stream.Printf("import Foundation\n");
stream.Printf("let address: UnsafeRawPointer? = UnsafeRawPointer(bitPattern: %llu)\n", addr.GetLoadAddress(target));
stream.Printf(R"___(
var returnString: String?
if let address = address, let ptr = malloc_zone_from_ptr(address) {
returnString = String(format: "%%p heap pointer, (0x%%x bytes), zone: %%p", Int(bitPattern: address), malloc_good_size(malloc_size(address)), Int(bitPattern: ptr))
}
guard let string = returnString else {
return ""
}
return string
)___");
// stream.Printf("uintptr_t ptr_t = %llu;", addr.GetLoadAddress(target));
// stream.Printf("void *ptr = (void *)ptr_t;");
// stream.Printf(R"___(
// @import CoreFoundation;
// char name[80];
// if ((void*)malloc_zone_from_ptr(ptr)) {
// size_t a = (size_t)malloc_good_size((size_t)malloc_size(ptr));
// void *p = (void*)malloc_zone_from_ptr(ptr);
// sprintf(name, "%%p heap pointer, (0x%%zx bytes), zone: %%p", ptr, a, p);
// } else {
// memset(name,'\0',sizeof(name));
// }
// bool isReturn = strcmp(name,"") == 0;
// )___");
// stream.Printf("isReturn ? nil : name;");
lldb::SBExpressionOptions eval_options;
// eval_options.SetLanguage(lldb::eLanguageTypeObjC_plus_plus);
eval_options.SetLanguage(lldb::eLanguageTypeSwift);
eval_options.SetAutoApplyFixIts(false);
eval_options.SetGenerateDebugInfo(false);
// 执行它支持的语言的代码
// lldb调试环境
SBValue value = target.EvaluateExpression(stream.GetData(), eval_options);
SBStream returnStream;
if (value.GetError().Success()) {
value.GetDescription(returnStream);
return std::make_tuple(true, [NSString stringWithUTF8String:returnStream.GetData()].UTF8String);
} else {
value.GetError().GetDescription(returnStream);
}
return std::make_tuple(false, nullptr);
}
bool CatAddressCommand::DoExecute(lldb::SBDebugger debugger, char **command,
lldb::SBCommandReturnObject & result) {
SBTarget target = debugger.GetSelectedTarget();
SBThread thread = target.GetProcess().GetSelectedThread();
if (!thread.IsValid()) {
result.SetError("Expects an address");
//false:命令处理失败
return false;
}
// 0x -> 地址 -》SBAddress
// 0x10000
NSString *cmd = [NSString stringWithCString:*command encoding:NSUTF8StringEncoding];
if ( !cmd || cmd.length < 1) {
result.SetError("Expects an address");
return false;
}
long address = 0;
char* end = nullptr;
if ([cmd.lowercaseString hasPrefix:@"0x"]) {
address = strtol(*command, &end, 16);
} else {
address = strtol(*command, &end, 10);
}
bool foundAddress = false;
const char *returnDescription = nullptr ;
SBAddress addr = target.ResolveLoadAddress(address);
// mach-o 所在的Section addr.GetSection()
// 1. 扫描Mach-o
if (addr.GetSection().IsValid()) {
std::tuple tuple = tryMachOAddress(addr, target);
foundAddress = std::get<0>(tuple);
returnDescription = std::get<1>(tuple);
}
// 2. 扫描stack
if (returnDescription == nullptr) {
std::tuple tuple = tryStackAddress(addr, target);
foundAddress = std::get<0>(tuple);
returnDescription = std::get<1>(tuple);
}
// 3. 扫描堆
if (returnDescription == nullptr) {
std::tuple tuple = tryHeapAddress(addr, target);
foundAddress = std::get<0>(tuple);
returnDescription = std::get<1>(tuple);
}
SBStream stream = SBStream();
if (foundAddress) {
stream.Printf("address:%s, %s", *command, returnDescription);
result.AppendMessage(stream.GetData());
//true:命令处理成功
return true;
} else {
stream.Printf("Couldn't find address, reverting to \"image lookup -a %s\"", *command);
result.AppendMessage(stream.GetData());
return false;
}
}
}
// 一个疗程
// 苹果的验证机制 -》 SIP -〉自编译的lldb.dylib
// SIP -〉还是不行 -》签名的事
// 继承SBCommandPluginInterface
// 动态库插件的mach-o扫描符号 lldb::PluginInitialize
// SBDebugger: 当前的调试器
// 干什么?自定义的命令挂载上
namespace lldb {
bool PluginInitialize(SBDebugger debugger) {
// SBCommandInterpreter :
lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
// p
// po 单个关键字的命令
// br set 多个关键字的命令
// cat address <地址> :地址所在的空间:mach-o、堆上、栈上
// help cat
// help cat address
lldb::SBCommand cmd = interpreter.AddMultiwordCommand("cat", NULL);
cmd.AddCommand("address", new LG::CatAddressCommand(@"Cat !"), "cat address ---- ");
return true;
}
}
测试用例
#import
#import "LGCatAddress.h"
@interface LGCatAddressTests : XCTestCase
{
lldb::SBDebugger _debugger;
lldb::SBTarget _target;
lldb::SBProcess _process;
lldb::SBCommandInterpreter _interp;
}
@end
@implementation LGCatAddressTests
- (void)setUp {
// 初始化SBDebugger -》加载一个可执行文件-〉验证我们的插件
lldb::SBDebugger::Initialize();
_debugger = lldb::SBDebugger::Create();
_debugger.SetAsync(false);
// 1. 初始化target
_target = _debugger.CreateTargetWithFileAndArch("/Users/xiaokai/Desktop/逻辑IOS/iOS\ 高级强化班/第十四节课、单元测试与UI测试/上课代码/02-插件判断地址在堆区/LGCatAddress/TestExec/a.out", "x86_64");
_target.BreakpointCreateByLocation("test.c", 17);
_interp = _debugger.GetCommandInterpreter();
}
- (void)testExample {
_process = _target.LaunchSimple(nil, nil, "/Users/xiaokai/Desktop/逻辑IOS/iOS\ 高级强化班/第十四节课、单元测试与UI测试/上课代码/02-插件判断地址在堆区/LGCatAddress/TestExec");
lldb::SBCommandReturnObject result;
// _interp.HandleCommand("r", result);
XCTAssertTrue(_process.GetNumThreads() > 0);
lldb::SBThread thread = _process.GetThreadAtIndex(0);
const char * name = thread.GetFrameAtIndex(0).GetFunctionName();
// 测试代码
// 手动的命令解释器+CatAddressCommand
// LG::CatAddressCommand lgCmd(@"zhin笨");
// lldb::SBCommand cmd = _interp.AddMultiwordCommand("cat", NULL);
// cmd.AddCommand("address", &lgCmd, "cat address");
// 调用我的cat address
_interp.HandleCommand("plugin load /Users/ws/Library/Developer/Xcode/DerivedData/LGCatAddress-gwqawrzsvandhqawsrpvhsxvjifb/Build/Products/Debug/libLGCatAddress.dylib", result);
NSLog(@"%s", result.GetOutput());
_interp.HandleCommand("cat address 0x10000", result);
NSLog(@"%s", result.GetOutput());
lldb::SBStream stream;
// cat address (po m)地址
_interp.HandleCommand("po m", result);
stream.Printf("cat address %s", result.GetOutput());
_interp.HandleCommand(stream.GetData(), result);
NSLog(@"%s", result.GetOutput());
// cat address (po b)地址
_interp.HandleCommand("po b", result);
lldb::SBStream bstream;
bstream.Printf("cat address %s", result.GetOutput());
_interp.HandleCommand(bstream.GetData(), result);
NSLog(@"%s", result.GetOutput());
// cat address (po p)地址 --malloc
_interp.HandleCommand("po p", result);
lldb::SBStream pstream;
pstream.Printf("cat address %s", result.GetOutput());
_interp.HandleCommand(pstream.GetData(), result);
NSLog(@"%s", result.GetOutput());
}
@end