LLDB查詢對象存儲區(qū)域(mach-o 棧區(qū) 堆區(qū))
#import "LGCatAddress.h"
// 1. C++插件
// 2. 模版
// 3. lldb + 基本上所有mach-o,執(zhí)行其他代碼信息
// 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<bool, const char *> 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<bool, const char *> 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);
}
// 插件執(zhí)行的環(huán)境
// 執(zhí)行插件所在的調試環(huán)境
// lldb -》 運行在一個進程上
// exec -〉 運行在一個進程上 lldb -》debugserver
// lldb -> 加載這個插件的-》調代碼-〉cat address
// 2個環(huán)境 -》 porcess -〉 2堆
// 開發(fā)porcess
//hmap
// 執(zhí)行插件-》調試環(huán)境-〉執(zhí)行相關的判斷代碼
// 自定義協議 + 客戶端 服務端 大廠用 人力物力成本大 引擎
// 動態(tài)庫 + 蘋果點頭
std::tuple<bool, const char *> 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);
// 執(zhí)行它支持的語言的代碼
// lldb調試環(huán)境
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<bool, const char *> tuple = tryMachOAddress(addr, target);
foundAddress = std::get<0>(tuple);
returnDescription = std::get<1>(tuple);
}
// 2. 掃描stack
if (returnDescription == nullptr) {
std::tuple<bool, const char *> tuple = tryStackAddress(addr, target);
foundAddress = std::get<0>(tuple);
returnDescription = std::get<1>(tuple);
}
// 3. 掃描堆
if (returnDescription == nullptr) {
std::tuple<bool, const char *> 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
// 動態(tài)庫插件的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 <XCTest/XCTest.h>
#import "LGCatAddress.h"
@interface LGCatAddressTests : XCTestCase
{
lldb::SBDebugger _debugger;
lldb::SBTarget _target;
lldb::SBProcess _process;
lldb::SBCommandInterpreter _interp;
}
@end
@implementation LGCatAddressTests
- (void)setUp {
// 初始化SBDebugger -》加載一個可執(zhí)行文件-〉驗證我們的插件
lldb::SBDebugger::Initialize();
_debugger = lldb::SBDebugger::Create();
_debugger.SetAsync(false);
// 1. 初始化target
_target = _debugger.CreateTargetWithFileAndArch("/Users/xiaokai/Desktop/邏輯IOS/iOS\ 高級強化班/第十四節(jié)課、單元測試與UI測試/上課代碼/02-插件判斷地址在堆區(qū)/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\ 高級強化班/第十四節(jié)課、單元測試與UI測試/上課代碼/02-插件判斷地址在堆區(qū)/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