动态库注入方式锁分析

This commit is contained in:
machunyu 2022-11-20 16:15:29 +08:00
parent d40a0fdb3d
commit 1491ef3061
4 changed files with 82 additions and 22 deletions

View File

@ -57,7 +57,7 @@
"description": "%deadlockdetect.taskDefinitions.options.cwd.description%" "description": "%deadlockdetect.taskDefinitions.options.cwd.description%"
}, },
"env": { "env": {
"type": "array", "type": "object",
"description": "%deadlockdetect.taskDefinitions.options.env.description%" "description": "%deadlockdetect.taskDefinitions.options.env.description%"
} }
} }

View File

@ -1,17 +1,23 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as child from 'child_process'; import * as child from 'child_process';
import {execDetect, testPwd, prase_yaml2html} from './utils'; import {execDetect, archs, testPwd, prase_yaml2html} from './utils';
import { dirname, join } from 'path'; import { dirname, join } from 'path';
import { kill } from 'process'; import { kill } from 'process';
import * as fs from 'fs'; import * as fs from 'fs';
type Options = { type Options = {
cwd?: string; cwd?: string;
env?: string[]; env?: {
[name: string]: any;
};
}; };
var isNotSupport = false;
const arch = archs.get(process.arch);
const binLoader: string = join(dirname(__dirname), "detect-tools/bincheck_loader.sh"); const binLoader: string = join(dirname(__dirname), "detect-tools/bincheck_loader.sh");
const hijackLib: string = join(dirname(__dirname), "detect-tools", arch? arch : "x86_64", "");
const hijackTool: string = join(dirname(__dirname), "detect-tools", arch? arch : "x86_64", "");
interface DetectTaskDefinition extends vscode.TaskDefinition { interface DetectTaskDefinition extends vscode.TaskDefinition {
command: string; command: string;
@ -19,14 +25,15 @@ interface DetectTaskDefinition extends vscode.TaskDefinition {
options?: Options; options?: Options;
} }
export class DetectTaskProvider implements vscode.TaskProvider { export class DetectTaskProvider implements vscode.TaskProvider {
static customBuildScriptType = 'deadlockdetect'; static customBuildScriptType = 'deadlockdetect';
private tasks: vscode.Task[] | undefined; private tasks: vscode.Task[] | undefined;
private sharedState: string | undefined; private sharedState: string | undefined;
private cmd: child.ChildProcessWithoutNullStreams | undefined; private cmd: child.ChildProcessWithoutNullStreams | undefined;
constructor(private workspaceRoot: string, private context: vscode.ExtensionContext) { } constructor(private workspaceRoot: string, private context: vscode.ExtensionContext, private _isNotSupport: boolean) {
isNotSupport = this._isNotSupport;
}
public async provideTasks(): Promise<vscode.Task[]> { public async provideTasks(): Promise<vscode.Task[]> {
return this.getTasks(); return this.getTasks();
@ -47,7 +54,6 @@ export class DetectTaskProvider implements vscode.TaskProvider {
} }
this.tasks = []; this.tasks = [];
var option: Options = { var option: Options = {
env: ["程序执行所需额外环境变量:如 DISPALY=1"],
}; };
this.tasks!.push(this.getTask("被检测可执行程序", ["可执行程序参数(可选)"], option)); this.tasks!.push(this.getTask("被检测可执行程序", ["可执行程序参数(可选)"], option));
return this.tasks; return this.tasks;
@ -77,16 +83,16 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
onDidClose?: vscode.Event<number> = this.closeEmitter.event; onDidClose?: vscode.Event<number> = this.closeEmitter.event;
private passwd: string | undefined; private passwd: string | undefined;
private checkedPid: number = 0; private checkedPid: number = 0;
private hadChecked: boolean = false;
constructor(private cmd: child.ChildProcessWithoutNullStreams | undefined, private context: vscode.ExtensionContext, private command: string, private args: string[], private option: Options, private getSharedState: () => string | undefined, private setSharedState: (state: string) => void) { constructor(private cmd: child.ChildProcessWithoutNullStreams | undefined, private context: vscode.ExtensionContext, private command: string, private args: string[], private option: Options, private getSharedState: () => string | undefined, private setSharedState: (state: string) => void) {
} }
open(initialDimensions: vscode.TerminalDimensions | undefined): void { open(initialDimensions: vscode.TerminalDimensions | undefined): void {
// At this point we can start using the terminal. // At this point we can start using the terminal.
if (this.command.length) { if (this.command.length) {
let found = false; let found = false;
vscode.workspace.workspaceFolders?.forEach((value)=>{ vscode.workspace.workspaceFolders?.forEach((value)=>{
// value.uri.fsPath // value.uri.fsPath
if(this.command[0] == '/' && !found){ if(this.command[0] === '/' && !found){
this.option.cwd = value.uri.fsPath; this.option.cwd = value.uri.fsPath;
found = true; found = true;
} }
@ -131,10 +137,35 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
this.closeEmitter.fire(0); this.closeEmitter.fire(0);
} }
} }
private hijackCheck(){
// 注入方式
child.exec(`${hijackTool} ${this.checkedPid}`, (err, o, e)=>{
if(o.match(/normal/)?.length){
const webPanel = vscode.window.createWebviewPanel(
'detectResultWebview',
"检测结果",
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true,
}
);
webPanel.webview.html = prase_yaml2html(this.command, this.context.extensionPath, webPanel.webview, o);
return;
}
vscode.window.showInformationMessage("未检测到相关锁信息");
});
}
close(): void { close(): void {
if(this.cmd){ if(this.cmd){
kill(this.checkedPid, 9); kill(this.checkedPid, 9);
vscode.window.showWarningMessage("检测任务已退出"); vscode.window.showWarningMessage("检测任务已退出");
if(isNotSupport && !this.hadChecked){
this.hadChecked = true;
this.hijackCheck();
}
} }
} }
@ -142,13 +173,18 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
private async doExec(): Promise<void> { private async doExec(): Promise<void> {
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
this.writeEmitter.fire('正在检测 ...\r\n'); this.writeEmitter.fire('正在检测 ...\r\n');
this.option.env?.forEach(e =>{ if(this.option.env){
let kv = e.split('=', 2); Object.keys(this.option.env).forEach(key => {
if(kv.length === 2){ let value = this.option.env?.[key];
process.env[kv[0]] = kv[1]; process.env[key] = value;
} });
}); }
let hadPid = false; let hadPid = false;
if(isNotSupport){
// 库注入
this.hadChecked = false;
process.env["LD_PRELOAD"] = hijackLib;
}
let coption: child.SpawnOptionsWithoutStdio = { let coption: child.SpawnOptionsWithoutStdio = {
env: process.env env: process.env
}; };
@ -180,6 +216,11 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
if(errmsg.length){ if(errmsg.length){
vscode.window.showErrorMessage(errmsg); vscode.window.showErrorMessage(errmsg);
} }
if(errmsg.length === 0 && isNotSupport && !this.hadChecked){
// 注入后锁分析
this.hadChecked = true;
this.hijackCheck();
}
this.writeEmitter.fire('被检测程序已终止 ... \r\n'); this.writeEmitter.fire('被检测程序已终止 ... \r\n');
const date = new Date(); const date = new Date();
this.setSharedState(date.toTimeString() + ' ' + date.toDateString()); this.setSharedState(date.toTimeString() + ' ' + date.toDateString());
@ -204,6 +245,10 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
if(result.length >= 4){ if(result.length >= 4){
hadPid = true; hadPid = true;
this.checkedPid = parseInt(result[3].trim()); this.checkedPid = parseInt(result[3].trim());
if(isNotSupport){
// 注入方式
return;
}
execDetect(this.passwd, result[1].trim(), this.checkedPid, (detectResult)=>{ execDetect(this.passwd, result[1].trim(), this.checkedPid, (detectResult)=>{
if(detectResult.match(/fatal/)?.length){ if(detectResult.match(/fatal/)?.length){
kill(this.checkedPid, 9); kill(this.checkedPid, 9);

View File

@ -5,7 +5,7 @@ import path = require('path');
import fs = require("fs"); import fs = require("fs");
import {DetectTaskProvider} from './detectTaskProvider'; import {DetectTaskProvider} from './detectTaskProvider';
import { homedir } from 'os'; import { homedir } from 'os';
import {exec} from 'child_process'; import {exec, execSync} from 'child_process';
import {testPwd} from './utils'; import {testPwd} from './utils';
import {doLocalPidLockAnalyse} from './analysePid'; import {doLocalPidLockAnalyse} from './analysePid';
// linux命令操作模块 // linux命令操作模块
@ -157,13 +157,27 @@ export function activate(context: vscode.ExtensionContext) {
vscode.window.showWarningMessage("检测环境失败, C/C++程序锁分析插件功能受限"); vscode.window.showWarningMessage("检测环境失败, C/C++程序锁分析插件功能受限");
return; return;
} }
exec(`grep -r "^CONFIG_BPF_EVENTS=" /boot/config-${o}`, (e1, o1, err1)=>{ var kernelOptions: string[] = [
if(e1){ "CONFIG_UPROBES=y",
vscode.window.showWarningMessage("由于内核限制, C/C++程序锁分析插件功能受限"); "CONFIG_UPROBE_EVENTS=y",
return; "CONFIG_TRACEPOINTS=y",
"CONFIG_PERF_EVENTS=y",
"CONFIG_BPF_EVENTS=y"
];
var isNotSupport = false;
fs.readFile(`/boot/config-${o}`, 'utf8', function (err, buffer) {
if(err){
isNotSupport = true;
}else{
var str = buffer.toString();
kernelOptions.forEach(elem=>{
if(!str.includes(elem)){
isNotSupport = true;
}
});
} }
const workspaceRoot = (vscode.workspace.workspaceFolders && (vscode.workspace.workspaceFolders.length > 0)) ? vscode.workspace.workspaceFolders[0].uri.fsPath : homedir(); const workspaceRoot = (vscode.workspace.workspaceFolders && (vscode.workspace.workspaceFolders.length > 0)) ? vscode.workspace.workspaceFolders[0].uri.fsPath : homedir();
detectTaskProvider = vscode.tasks.registerTaskProvider(DetectTaskProvider.customBuildScriptType, new DetectTaskProvider(workspaceRoot, context)); detectTaskProvider = vscode.tasks.registerTaskProvider(DetectTaskProvider.customBuildScriptType, new DetectTaskProvider(workspaceRoot, context, isNotSupport));
}); });
}); });
context.subscriptions.push(...[disposable,disposable0]); context.subscriptions.push(...[disposable,disposable0]);

View File

@ -3,10 +3,11 @@ import * as vscode from 'vscode';
import { dirname, join } from 'path'; import { dirname, join } from 'path';
import * as YAML from 'yaml'; import * as YAML from 'yaml';
const archs = new Map([ export const archs = new Map([
["x86_64", "x86_64"], ["x86_64", "x86_64"],
["x64", "x86_64"], ["x64", "x86_64"],
["arm64", "arm64"] ["arm64", "arm64"],
["aarch64", "arm64"]
]); ]);
const myArch = archs.get(process.arch); const myArch = archs.get(process.arch);