Skip to content

Commit f02b590

Browse files
committed
add golang elf parse and probe config file
1 parent 304d77a commit f02b590

File tree

11 files changed

+383
-79
lines changed

11 files changed

+383
-79
lines changed

rasp/jvm/JVMProbe/src/main/java/com/security/smith/client/Client.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.google.gson.JsonArray;
55
import com.google.gson.JsonElement;
66
import com.google.gson.JsonParser;
7+
import com.google.gson.GsonBuilder;
78
import com.security.smith.client.message.*;
89
import com.security.smith.common.ProcessHelper;
910
import com.security.smith.log.SmithLogger;
@@ -282,7 +283,8 @@ private void readMessage() {
282283
JsonArray jsonArray = jsonElement.getAsJsonArray();
283284
for (JsonElement element : jsonArray) {
284285
if (element.isJsonObject()) {
285-
Message message = new Gson().fromJson(element, Message.class);
286+
Gson gson = new GsonBuilder().registerTypeAdapter(Message.class, new MessageDeserializer()).create();
287+
Message message = gson.fromJson(element, Message.class);
286288
onMessage(message);
287289
}
288290
}

rasp/librasp/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ libc = "0.2.80"
1818
nix = "0.24"
1919
anyhow = "1.0.38"
2020
# elf
21-
goblin = "0.3.4"
21+
goblin = "0.8.0"
22+
byteorder = "1.0"
2223
# lru cache
2324
lru_time_cache = "0.11.8"
2425
memmap = "0.7.0"

rasp/librasp/src/golang.rs

+85-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use log::*;
22
use std::fs::File;
33
use std::{fs, path::PathBuf, process::Command};
4-
4+
use std::io::Read;
5+
use regex::Regex;
56
use anyhow::{anyhow, Result};
67
use goblin::elf::Elf;
78
use memmap::MmapOptions;
@@ -10,7 +11,9 @@ use crate::async_command::run_async_process;
1011
use crate::process::ProcessInfo;
1112
use crate::runtime::{ProbeCopy, ProbeState, ProbeStateInspect};
1213
use crate::settings;
14+
use crate::parse_elf::{find_by_section, find_by_symbol};
1315

16+
const GOLANG_SUPPORT_VERSION: u32 = 22;
1417
pub struct GolangProbeState {}
1518

1619
impl ProbeStateInspect for GolangProbeState {
@@ -105,10 +108,10 @@ pub fn golang_attach(pid: i32) -> Result<bool> {
105108
Ok(true)
106109
} else if es_code == 255 {
107110
let msg = format!(
108-
"golang attach exit code 255: {} {} {} {}",
109-
es_code, pid, &stdout, &stderr
111+
"golang attach exit code 255: {} {} {}",
112+
es_code, &stdout, &stderr
110113
);
111-
error!("{}", msg);
114+
error!("pid: {}, {}", pid, msg);
112115
Err(anyhow!("{}", msg))
113116
} else {
114117
let msg = format!(
@@ -123,7 +126,7 @@ pub fn golang_attach(pid: i32) -> Result<bool> {
123126
};
124127
}
125128

126-
pub fn golang_bin_inspect(bin_file: PathBuf) -> Result<u64> {
129+
pub fn golang_bin_inspect(bin_file: &PathBuf) -> Result<u64> {
127130
let metadata = match fs::metadata(bin_file.clone()) {
128131
Ok(md) => md,
129132
Err(e) => {
@@ -141,11 +144,86 @@ pub fn golang_bin_inspect(bin_file: PathBuf) -> Result<u64> {
141144
let shstrtab = elf.shdr_strtab;
142145
for section in elf.section_headers.iter() {
143146
let offset = section.sh_name;
144-
if let Some(name) = shstrtab.get(offset) {
145-
if name.unwrap() == ".gopclntab" {
147+
if let Some(name) = shstrtab.get_at(offset) {
148+
if name == ".gopclntab" {
146149
return Ok(size);
147150
}
148151
}
149152
}
150153
return Ok(0);
151154
}
155+
156+
pub fn parse_version(version: &String) -> Result<String> {
157+
let re = Regex::new(r"^go(\d+\.\d+)(?:\.\d+)?").unwrap();
158+
159+
// use regex to extract the version number from the string
160+
if let Some(captures) = re.captures(version) {
161+
if let Some(version_number) = captures.get(1) {
162+
let extracted_version = version_number.as_str();
163+
return Ok(extracted_version.to_string());
164+
}
165+
}
166+
return Err(anyhow::anyhow!("Failed to extract version number, from: {}", version));
167+
}
168+
169+
pub fn golang_version(bin_file: &PathBuf) -> Result<String> {
170+
let mut file = File::open(bin_file)?;
171+
let mut buffer = Vec::new();
172+
file.read_to_end(&mut buffer)?;
173+
174+
// parse elf
175+
let elf = match Elf::parse(&buffer) {
176+
Ok(elf) => elf,
177+
Err(err) => {
178+
let msg = format!(
179+
"Failed to parse ELF file: {}", err
180+
);
181+
warn!("{}", msg);
182+
return Err(anyhow!("{}", msg));
183+
}
184+
};
185+
186+
if let Ok(version) = find_by_section(&elf, &buffer, &file) {
187+
return parse_version(&version);
188+
} else {
189+
if let Ok(version) = find_by_symbol(&elf, &file) {
190+
return parse_version(&version);
191+
} else {
192+
return Err(anyhow!("get go version by section and symbol failed"));
193+
}
194+
}
195+
196+
}
197+
198+
pub fn check_golang_version(ver: &String) -> Result<()> {
199+
let major_minor: Option<(u32, u32)> = match ver.split('.').next() {
200+
Some(major_str) => {
201+
if let Ok(major) = major_str.parse::<u32>() {
202+
if let Some(minor_str) = ver.split('.').nth(1) {
203+
if let Ok(minor) = minor_str.parse::<u32>() {
204+
Some((major, minor))
205+
} else {
206+
None
207+
}
208+
} else {
209+
Some((major, 0))
210+
}
211+
} else {
212+
None
213+
}
214+
}
215+
None => None,
216+
};
217+
218+
if let Some((major, minor)) = major_minor {
219+
if major == 1 && minor <= GOLANG_SUPPORT_VERSION {
220+
return Ok(());
221+
} else {
222+
let msg = format!("Golang version too big: {}", ver);
223+
return Err(anyhow!(msg));
224+
}
225+
} else {
226+
let msg = format!("golang version cannot parse: {}", ver);
227+
return Err(anyhow!(msg));
228+
}
229+
}

rasp/librasp/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod process;
99
pub mod runtime;
1010
#[allow(non_snake_case)]
1111
pub mod settings;
12+
pub mod parse_elf;
1213

1314
pub mod async_command {
1415
use std::io::{BufRead, BufReader};

rasp/librasp/src/manager.rs

+35-53
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ use std::collections::HashMap;
22
use std::ffi::OsStr;
33
use std::fs;
44
use std::path::Path;
5-
5+
use std::os::unix::fs::PermissionsExt;
66
use anyhow::{anyhow, Result, Result as AnyhowResult};
77
use crossbeam::channel::Sender;
88
use fs_extra::dir::{copy, create_all, CopyOptions};
9-
use fs_extra::file::{copy as file_copy, CopyOptions as FileCopyOptions};
9+
use fs_extra::file::{copy as file_copy, remove, CopyOptions as FileCopyOptions};
1010
use libraspserver::proto::{PidMissingProbeConfig, ProbeConfigData};
1111
use log::*;
1212

1313
use crate::cpython::{python_attach, CPythonProbe, CPythonProbeState};
14-
use crate::golang::{golang_attach, GolangProbe, GolangProbeState};
14+
use crate::golang::{check_golang_version, golang_attach, GolangProbe, GolangProbeState};
1515
use crate::jvm::{check_java_version, java_attach, java_detach, JVMProbe, JVMProbeState};
1616
use crate::nodejs::{check_nodejs_version, nodejs_attach, NodeJSProbe};
1717
use crate::php::{php_attach, PHPProbeState};
@@ -41,8 +41,8 @@ impl RASPManager {
4141
) -> AnyhowResult<()> {
4242
debug!("starting comm with probe, target pid: {}", process_info.pid);
4343
let mnt_namespace = process_info.get_mnt_ns()?;
44-
let nspid = if let Some(nspid) = ProcessInfo::read_nspid(process_info.pid)? {
45-
nspid
44+
let nspid = if process_info.nspid != 0 {
45+
process_info.nspid
4646
} else {
4747
process_info.pid
4848
};
@@ -259,8 +259,8 @@ impl RASPManager {
259259
}
260260
}
261261

262-
serde_json::to_string(&valid_messages)?;
263-
//self.write_message_to_config_file(pid, nspid, valid_messages_string)?;
262+
let valid_messages_string = serde_json::to_string(&valid_messages)?;
263+
self.write_message_to_config_file(pid, nspid, valid_messages_string)?;
264264

265265
Ok(())
266266
}
@@ -324,9 +324,9 @@ impl RASPManager {
324324
let runtime_info = &process_info.runtime.clone().unwrap();
325325
let root_dir = format!("/proc/{}/root", process_info.pid);
326326
let pid = process_info.pid;
327-
ProcessInfo::read_nspid(pid)?.ok_or(anyhow!("can not read nspid: {}", pid))?;
327+
let nspid = process_info.nspid;
328328
// delete config
329-
// self.delete_config_file(pid, nspid)?;
329+
self.delete_config_file(pid, nspid)?;
330330
let attach_result = match runtime_info.name {
331331
"JVM" => match JVMProbeState::inspect_process(process_info)? {
332332
ProbeState::Attached => {
@@ -371,7 +371,7 @@ impl RASPManager {
371371
let to = format!("{}{}",root_dir.clone(), settings::RASP_JAVA_AGENT_BIN());
372372
let _ = self.copy_file_from_to_dest(settings::RASP_JAVA_JATTACH_BIN(), root_dir.clone());
373373
let _ = self.copy_file_from_to_dest(settings::RASP_JAVA_AGENT_BIN(), root_dir.clone());
374-
info!("copy from jattach/SmithAgent.jar to {}", to.clone());
374+
info!("copy from java/SmithAgent.jar to {}", to.clone());
375375
}
376376
}
377377
Err(e) => {
@@ -430,6 +430,14 @@ impl RASPManager {
430430
Ok(true)
431431
}
432432
ProbeState::NotAttach => {
433+
if !runtime_info.version.is_empty() {
434+
match check_golang_version(&runtime_info.version) {
435+
Ok(_) => {}
436+
Err(e) => {
437+
return Err(anyhow!(e));
438+
}
439+
}
440+
}
433441
let mut golang_attach = |pid: i32, bpf: bool| -> AnyhowResult<bool> {
434442
if bpf {
435443
if let Some(bpf_manager) = self.ebpf_comm.as_mut() {
@@ -881,66 +889,40 @@ impl MntNamespaceTracer {
881889
}
882890

883891
impl RASPManager {
884-
/*
885892
pub fn write_message_to_config_file(
886893
&self,
887894
pid: i32,
888895
nspid: i32,
889896
message: String,
890897
) -> AnyhowResult<()> {
891-
let config_dir = "/var/run/elkeid_rasp";
898+
let config_dir = format!("/proc/{}/root/var/run/elkeid_rasp", pid);
892899
let config_path = format!("{}/{}.json", config_dir, nspid);
893900
let config_path_bak = format!("{}.bak", config_path);
894-
debug!("write message to {} {}", config_path_bak, message);
895-
crate::async_command::run_async_process(
896-
Command::new(crate::settings::RASP_NS_ENTER_BIN()).args([
897-
"-m",
898-
"-t",
899-
pid.to_string().as_str(),
900-
"sh",
901-
"-c",
902-
"PATH=/bin:/usr/bin:/sbin",
903-
format!(
904-
"mkdir -p {} && echo '{}' > {} && mv {} {}",
905-
config_dir, message, config_path_bak, config_path_bak, config_path
906-
)
907-
.as_str(),
908-
]),
909-
)?;
910-
let ns_thread = thread::Builder::new().spawn(move || -> AnyhowResult<()> {
911-
debug!("switch namespace");
912-
libraspserver::ns::switch_namespace(pid);
913-
if !Path::new(&config_dir).exists() {
914-
fs_extra::dir::create(config_dir, true)?;
915-
}
916-
fs_extra::file::write_all(&config_path_bak, message.as_str())?;
917-
let mut option = fs_extra::file::CopyOptions::new();
918-
option.overwrite = true;
919-
fs_extra::file::move_file(config_path_bak, config_path, &option)?;
920-
Ok(())
921-
}).unwrap();
922-
ns_thread.join()?;
901+
info!("write message to {} {}", config_path_bak, message);
902+
903+
if !Path::new(&config_dir).exists() {
904+
fs_extra::dir::create(&config_dir, true)?;
905+
}
906+
fs::set_permissions(&config_dir, fs::Permissions::from_mode(0o666))?;
907+
fs_extra::file::write_all(&config_path_bak, message.as_str())?;
908+
fs::set_permissions(&config_path_bak, fs::Permissions::from_mode(0o777))?;
909+
let mut option = fs_extra::file::CopyOptions::new();
910+
option.overwrite = true;
911+
fs_extra::file::move_file(config_path_bak, config_path, &option)?;
912+
info!("write message success");
923913

924914
Ok(())
925915
}
926916

927917
pub fn delete_config_file(&self, pid: i32, nspid: i32) -> AnyhowResult<()> {
928-
let config_path = format!("/var/run/elkeid_rasp/{}.json", nspid);
918+
919+
let config_path = format!("/proc/{}/root/var/run/elkeid_rasp/{}.json", pid, nspid);
929920
if Path::new(&config_path).exists() {
930-
crate::async_command::run_async_process(
931-
Command::new(crate::settings::RASP_NS_ENTER_BIN()).args([
932-
"-m",
933-
"-t",
934-
pid.to_string().as_str(),
935-
"sh",
936-
"-c",
937-
format!("rm {}", config_path).as_str(),
938-
]),
939-
)?;
921+
info!("delete config file: {}", config_path);
922+
remove(config_path)?
940923
}
941924
Ok(())
942925
}
943-
*/
944926
}
945927

946928
fn read_dir<P>(path: P) -> AnyhowResult<Vec<fs::DirEntry>>

0 commit comments

Comments
 (0)