Struct udmp_parser::MemBlock
source · pub struct MemBlock<'a> {
pub range: Range<u64>,
pub allocation_base: u64,
pub allocation_protect: u32,
pub state: u32,
pub protect: u32,
pub type_: u32,
pub data: &'a [u8],
}
Expand description
Fields§
§range: Range<u64>
Range over the start/end address of the memory region.
allocation_base: u64
The base of the allocation that gave life to this memory region.
allocation_protect: u32
The page protection used at allocation time.
state: u32
The state of the memory region. See State.
protect: u32
The page protection currently applied to the memory region.
type_: u32
The type of memory region. See Type.
data: &'a [u8]
The MemBlock
’s data.
Implementations§
source§impl<'a> MemBlock<'a>
impl<'a> MemBlock<'a>
sourcepub fn is_readable(&self) -> bool
pub fn is_readable(&self) -> bool
Is the memory region readable?
sourcepub fn is_writable(&self) -> bool
pub fn is_writable(&self) -> bool
Is the memory region writable?
sourcepub fn is_executable(&self) -> bool
pub fn is_executable(&self) -> bool
Is the memory region executable?
sourcepub fn state_as_str(&self) -> &str
pub fn state_as_str(&self) -> &str
Stringify the memory region state.
Examples found in repository?
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
fn main() -> Result<(), String> {
// If we don't have any arguments, display the help.
if env::args().len() == 1 {
help();
return Ok(());
}
// Parse the command line arguments.
let cli = parse_args()?;
// Let's try to parse the dump file specified by the user.
let dump = UserDumpParser::new(cli.dump_path).map_err(|e| e.to_string())?;
// Do we want to display modules?
if cli.show_mods || cli.show_all {
println!("Loaded modules:");
// Iterate through the module and display their base address and path.
for (base, module) in dump.modules() {
println!("{:016x}: {}", base, module.path.display());
}
}
// Do we want the memory map?
if cli.show_memmap || cli.show_all {
println!("Memory map:");
// Iterate over the memory blocks.
for block in dump.mem_blocks().values() {
// Grab the string representation about its state, type, protection.
let state = block.state_as_str();
let type_ = block.type_as_str();
let protect = block.protect_as_str();
// Print it all out.
print!(
"{:016x} {:016x} {:016x} {:11} {:11} {:22}",
block.range.start,
block.range.end,
block.len(),
type_,
state,
protect
);
// Do we have a module that exists at this address?
let module = dump.get_module(block.range.start);
// If we do, then display its name / path.
if let Some(module) = module {
print!(
" [{}; \"{}\"]",
module.file_name().unwrap(),
module.path.display()
);
}
// Do we have data with this block? If so display the first few
// bytes.
if block.data.len() >= 4 {
print!(
" {:02x} {:02x} {:02x} {:02x}...",
block.data[0], block.data[1], block.data[2], block.data[3]
);
}
println!();
}
}
// Do we want threads?
if cli.show_threads || cli.show_all {
println!("Threads:");
// Grab the foreground tid.
let foreground_tid = dump.foreground_tid;
// Iterate through all the threads.
for (tid, thread) in dump.threads() {
// If the user specified a pid..
if let Some(wanted_tid) = cli.thread {
// .. skip an threads that don't match what the user wants..
if *tid != wanted_tid {
continue;
}
// Otherwise we keep going.
}
// If the user only wants the main thread, and we haven't found it,
// skip this thread until we find it.
if cli.show_foreground_thread
&& *tid != foreground_tid.expect("no foreground thread id in dump")
{
continue;
}
// Print out the thread info.
println!("TID {}, TEB {:016x}", tid, thread.teb);
println!("Context:");
println!("{}", thread.context());
}
}
// Do we want to dump memory?
if let Some(address) = cli.address {
println!("Memory:");
// Try to find a block that contains `address`.
let block = dump.get_mem_block(address);
// If we have one..
if let Some(block) = block {
// .. and it has data, dump it..
if let Some(data) = block.data_from(address) {
println!("{:016x} -> {:016x}", address, block.end_addr());
hexdump(address, data.iter().take(0x1_00).copied());
}
// .. otherwise, inform the user..
else {
println!(
"The memory at {:016x} (from block {:016x} -> {:016x}) has no backing data",
address, block.range.start, block.range.end
);
}
}
// .. otherwise, inform he user.
else {
println!("No memory block were found for {:016x}", address);
}
}
// All right, enough for today.
Ok(())
}
sourcepub fn type_as_str(&self) -> &str
pub fn type_as_str(&self) -> &str
Stringify the memory region type.
Examples found in repository?
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
fn main() -> Result<(), String> {
// If we don't have any arguments, display the help.
if env::args().len() == 1 {
help();
return Ok(());
}
// Parse the command line arguments.
let cli = parse_args()?;
// Let's try to parse the dump file specified by the user.
let dump = UserDumpParser::new(cli.dump_path).map_err(|e| e.to_string())?;
// Do we want to display modules?
if cli.show_mods || cli.show_all {
println!("Loaded modules:");
// Iterate through the module and display their base address and path.
for (base, module) in dump.modules() {
println!("{:016x}: {}", base, module.path.display());
}
}
// Do we want the memory map?
if cli.show_memmap || cli.show_all {
println!("Memory map:");
// Iterate over the memory blocks.
for block in dump.mem_blocks().values() {
// Grab the string representation about its state, type, protection.
let state = block.state_as_str();
let type_ = block.type_as_str();
let protect = block.protect_as_str();
// Print it all out.
print!(
"{:016x} {:016x} {:016x} {:11} {:11} {:22}",
block.range.start,
block.range.end,
block.len(),
type_,
state,
protect
);
// Do we have a module that exists at this address?
let module = dump.get_module(block.range.start);
// If we do, then display its name / path.
if let Some(module) = module {
print!(
" [{}; \"{}\"]",
module.file_name().unwrap(),
module.path.display()
);
}
// Do we have data with this block? If so display the first few
// bytes.
if block.data.len() >= 4 {
print!(
" {:02x} {:02x} {:02x} {:02x}...",
block.data[0], block.data[1], block.data[2], block.data[3]
);
}
println!();
}
}
// Do we want threads?
if cli.show_threads || cli.show_all {
println!("Threads:");
// Grab the foreground tid.
let foreground_tid = dump.foreground_tid;
// Iterate through all the threads.
for (tid, thread) in dump.threads() {
// If the user specified a pid..
if let Some(wanted_tid) = cli.thread {
// .. skip an threads that don't match what the user wants..
if *tid != wanted_tid {
continue;
}
// Otherwise we keep going.
}
// If the user only wants the main thread, and we haven't found it,
// skip this thread until we find it.
if cli.show_foreground_thread
&& *tid != foreground_tid.expect("no foreground thread id in dump")
{
continue;
}
// Print out the thread info.
println!("TID {}, TEB {:016x}", tid, thread.teb);
println!("Context:");
println!("{}", thread.context());
}
}
// Do we want to dump memory?
if let Some(address) = cli.address {
println!("Memory:");
// Try to find a block that contains `address`.
let block = dump.get_mem_block(address);
// If we have one..
if let Some(block) = block {
// .. and it has data, dump it..
if let Some(data) = block.data_from(address) {
println!("{:016x} -> {:016x}", address, block.end_addr());
hexdump(address, data.iter().take(0x1_00).copied());
}
// .. otherwise, inform the user..
else {
println!(
"The memory at {:016x} (from block {:016x} -> {:016x}) has no backing data",
address, block.range.start, block.range.end
);
}
}
// .. otherwise, inform he user.
else {
println!("No memory block were found for {:016x}", address);
}
}
// All right, enough for today.
Ok(())
}
sourcepub fn protect_as_str(&self) -> String
pub fn protect_as_str(&self) -> String
Stringify the memory region protection.
Examples found in repository?
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
fn main() -> Result<(), String> {
// If we don't have any arguments, display the help.
if env::args().len() == 1 {
help();
return Ok(());
}
// Parse the command line arguments.
let cli = parse_args()?;
// Let's try to parse the dump file specified by the user.
let dump = UserDumpParser::new(cli.dump_path).map_err(|e| e.to_string())?;
// Do we want to display modules?
if cli.show_mods || cli.show_all {
println!("Loaded modules:");
// Iterate through the module and display their base address and path.
for (base, module) in dump.modules() {
println!("{:016x}: {}", base, module.path.display());
}
}
// Do we want the memory map?
if cli.show_memmap || cli.show_all {
println!("Memory map:");
// Iterate over the memory blocks.
for block in dump.mem_blocks().values() {
// Grab the string representation about its state, type, protection.
let state = block.state_as_str();
let type_ = block.type_as_str();
let protect = block.protect_as_str();
// Print it all out.
print!(
"{:016x} {:016x} {:016x} {:11} {:11} {:22}",
block.range.start,
block.range.end,
block.len(),
type_,
state,
protect
);
// Do we have a module that exists at this address?
let module = dump.get_module(block.range.start);
// If we do, then display its name / path.
if let Some(module) = module {
print!(
" [{}; \"{}\"]",
module.file_name().unwrap(),
module.path.display()
);
}
// Do we have data with this block? If so display the first few
// bytes.
if block.data.len() >= 4 {
print!(
" {:02x} {:02x} {:02x} {:02x}...",
block.data[0], block.data[1], block.data[2], block.data[3]
);
}
println!();
}
}
// Do we want threads?
if cli.show_threads || cli.show_all {
println!("Threads:");
// Grab the foreground tid.
let foreground_tid = dump.foreground_tid;
// Iterate through all the threads.
for (tid, thread) in dump.threads() {
// If the user specified a pid..
if let Some(wanted_tid) = cli.thread {
// .. skip an threads that don't match what the user wants..
if *tid != wanted_tid {
continue;
}
// Otherwise we keep going.
}
// If the user only wants the main thread, and we haven't found it,
// skip this thread until we find it.
if cli.show_foreground_thread
&& *tid != foreground_tid.expect("no foreground thread id in dump")
{
continue;
}
// Print out the thread info.
println!("TID {}, TEB {:016x}", tid, thread.teb);
println!("Context:");
println!("{}", thread.context());
}
}
// Do we want to dump memory?
if let Some(address) = cli.address {
println!("Memory:");
// Try to find a block that contains `address`.
let block = dump.get_mem_block(address);
// If we have one..
if let Some(block) = block {
// .. and it has data, dump it..
if let Some(data) = block.data_from(address) {
println!("{:016x} -> {:016x}", address, block.end_addr());
hexdump(address, data.iter().take(0x1_00).copied());
}
// .. otherwise, inform the user..
else {
println!(
"The memory at {:016x} (from block {:016x} -> {:016x}) has no backing data",
address, block.range.start, block.range.end
);
}
}
// .. otherwise, inform he user.
else {
println!("No memory block were found for {:016x}", address);
}
}
// All right, enough for today.
Ok(())
}
sourcepub fn data_from(&self, addr: u64) -> Option<&[u8]>
pub fn data_from(&self, addr: u64) -> Option<&[u8]>
Get a slice over the MemBlock
’s data from its absolute address.
If the dump had a memory block of size 4 bytes starting at address
0xdead then calling data_from(0xdead+1)
returns a slice over the
last 3 bytes of the memory block. This is useful when you don’t need
to reason about offsets.
Examples found in repository?
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
fn main() -> Result<(), String> {
// If we don't have any arguments, display the help.
if env::args().len() == 1 {
help();
return Ok(());
}
// Parse the command line arguments.
let cli = parse_args()?;
// Let's try to parse the dump file specified by the user.
let dump = UserDumpParser::new(cli.dump_path).map_err(|e| e.to_string())?;
// Do we want to display modules?
if cli.show_mods || cli.show_all {
println!("Loaded modules:");
// Iterate through the module and display their base address and path.
for (base, module) in dump.modules() {
println!("{:016x}: {}", base, module.path.display());
}
}
// Do we want the memory map?
if cli.show_memmap || cli.show_all {
println!("Memory map:");
// Iterate over the memory blocks.
for block in dump.mem_blocks().values() {
// Grab the string representation about its state, type, protection.
let state = block.state_as_str();
let type_ = block.type_as_str();
let protect = block.protect_as_str();
// Print it all out.
print!(
"{:016x} {:016x} {:016x} {:11} {:11} {:22}",
block.range.start,
block.range.end,
block.len(),
type_,
state,
protect
);
// Do we have a module that exists at this address?
let module = dump.get_module(block.range.start);
// If we do, then display its name / path.
if let Some(module) = module {
print!(
" [{}; \"{}\"]",
module.file_name().unwrap(),
module.path.display()
);
}
// Do we have data with this block? If so display the first few
// bytes.
if block.data.len() >= 4 {
print!(
" {:02x} {:02x} {:02x} {:02x}...",
block.data[0], block.data[1], block.data[2], block.data[3]
);
}
println!();
}
}
// Do we want threads?
if cli.show_threads || cli.show_all {
println!("Threads:");
// Grab the foreground tid.
let foreground_tid = dump.foreground_tid;
// Iterate through all the threads.
for (tid, thread) in dump.threads() {
// If the user specified a pid..
if let Some(wanted_tid) = cli.thread {
// .. skip an threads that don't match what the user wants..
if *tid != wanted_tid {
continue;
}
// Otherwise we keep going.
}
// If the user only wants the main thread, and we haven't found it,
// skip this thread until we find it.
if cli.show_foreground_thread
&& *tid != foreground_tid.expect("no foreground thread id in dump")
{
continue;
}
// Print out the thread info.
println!("TID {}, TEB {:016x}", tid, thread.teb);
println!("Context:");
println!("{}", thread.context());
}
}
// Do we want to dump memory?
if let Some(address) = cli.address {
println!("Memory:");
// Try to find a block that contains `address`.
let block = dump.get_mem_block(address);
// If we have one..
if let Some(block) = block {
// .. and it has data, dump it..
if let Some(data) = block.data_from(address) {
println!("{:016x} -> {:016x}", address, block.end_addr());
hexdump(address, data.iter().take(0x1_00).copied());
}
// .. otherwise, inform the user..
else {
println!(
"The memory at {:016x} (from block {:016x} -> {:016x}) has no backing data",
address, block.range.start, block.range.end
);
}
}
// .. otherwise, inform he user.
else {
println!("No memory block were found for {:016x}", address);
}
}
// All right, enough for today.
Ok(())
}
sourcepub fn start_addr(&self) -> u64
pub fn start_addr(&self) -> u64
Get the address of where this MemBlock
was at in memory.
sourcepub fn end_addr(&self) -> u64
pub fn end_addr(&self) -> u64
Get the end address of where this MemBlock
was at in memory.
Note that the underlying range is not inclusive, so this address is pointing right after the last byte’s address.
Examples found in repository?
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
fn main() -> Result<(), String> {
// If we don't have any arguments, display the help.
if env::args().len() == 1 {
help();
return Ok(());
}
// Parse the command line arguments.
let cli = parse_args()?;
// Let's try to parse the dump file specified by the user.
let dump = UserDumpParser::new(cli.dump_path).map_err(|e| e.to_string())?;
// Do we want to display modules?
if cli.show_mods || cli.show_all {
println!("Loaded modules:");
// Iterate through the module and display their base address and path.
for (base, module) in dump.modules() {
println!("{:016x}: {}", base, module.path.display());
}
}
// Do we want the memory map?
if cli.show_memmap || cli.show_all {
println!("Memory map:");
// Iterate over the memory blocks.
for block in dump.mem_blocks().values() {
// Grab the string representation about its state, type, protection.
let state = block.state_as_str();
let type_ = block.type_as_str();
let protect = block.protect_as_str();
// Print it all out.
print!(
"{:016x} {:016x} {:016x} {:11} {:11} {:22}",
block.range.start,
block.range.end,
block.len(),
type_,
state,
protect
);
// Do we have a module that exists at this address?
let module = dump.get_module(block.range.start);
// If we do, then display its name / path.
if let Some(module) = module {
print!(
" [{}; \"{}\"]",
module.file_name().unwrap(),
module.path.display()
);
}
// Do we have data with this block? If so display the first few
// bytes.
if block.data.len() >= 4 {
print!(
" {:02x} {:02x} {:02x} {:02x}...",
block.data[0], block.data[1], block.data[2], block.data[3]
);
}
println!();
}
}
// Do we want threads?
if cli.show_threads || cli.show_all {
println!("Threads:");
// Grab the foreground tid.
let foreground_tid = dump.foreground_tid;
// Iterate through all the threads.
for (tid, thread) in dump.threads() {
// If the user specified a pid..
if let Some(wanted_tid) = cli.thread {
// .. skip an threads that don't match what the user wants..
if *tid != wanted_tid {
continue;
}
// Otherwise we keep going.
}
// If the user only wants the main thread, and we haven't found it,
// skip this thread until we find it.
if cli.show_foreground_thread
&& *tid != foreground_tid.expect("no foreground thread id in dump")
{
continue;
}
// Print out the thread info.
println!("TID {}, TEB {:016x}", tid, thread.teb);
println!("Context:");
println!("{}", thread.context());
}
}
// Do we want to dump memory?
if let Some(address) = cli.address {
println!("Memory:");
// Try to find a block that contains `address`.
let block = dump.get_mem_block(address);
// If we have one..
if let Some(block) = block {
// .. and it has data, dump it..
if let Some(data) = block.data_from(address) {
println!("{:016x} -> {:016x}", address, block.end_addr());
hexdump(address, data.iter().take(0x1_00).copied());
}
// .. otherwise, inform the user..
else {
println!(
"The memory at {:016x} (from block {:016x} -> {:016x}) has no backing data",
address, block.range.start, block.range.end
);
}
}
// .. otherwise, inform he user.
else {
println!("No memory block were found for {:016x}", address);
}
}
// All right, enough for today.
Ok(())
}
sourcepub fn len(&self) -> u64
pub fn len(&self) -> u64
Get the size of the MemBlock
.
Note that a region of memory can exists without having any data
associated with it. This method returns the range len, not data
’s len.
An example is a memory region mapped as PAGE_NOACCESS
; it exists in
the address space but has no content.
Examples found in repository?
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
fn main() -> Result<(), String> {
// If we don't have any arguments, display the help.
if env::args().len() == 1 {
help();
return Ok(());
}
// Parse the command line arguments.
let cli = parse_args()?;
// Let's try to parse the dump file specified by the user.
let dump = UserDumpParser::new(cli.dump_path).map_err(|e| e.to_string())?;
// Do we want to display modules?
if cli.show_mods || cli.show_all {
println!("Loaded modules:");
// Iterate through the module and display their base address and path.
for (base, module) in dump.modules() {
println!("{:016x}: {}", base, module.path.display());
}
}
// Do we want the memory map?
if cli.show_memmap || cli.show_all {
println!("Memory map:");
// Iterate over the memory blocks.
for block in dump.mem_blocks().values() {
// Grab the string representation about its state, type, protection.
let state = block.state_as_str();
let type_ = block.type_as_str();
let protect = block.protect_as_str();
// Print it all out.
print!(
"{:016x} {:016x} {:016x} {:11} {:11} {:22}",
block.range.start,
block.range.end,
block.len(),
type_,
state,
protect
);
// Do we have a module that exists at this address?
let module = dump.get_module(block.range.start);
// If we do, then display its name / path.
if let Some(module) = module {
print!(
" [{}; \"{}\"]",
module.file_name().unwrap(),
module.path.display()
);
}
// Do we have data with this block? If so display the first few
// bytes.
if block.data.len() >= 4 {
print!(
" {:02x} {:02x} {:02x} {:02x}...",
block.data[0], block.data[1], block.data[2], block.data[3]
);
}
println!();
}
}
// Do we want threads?
if cli.show_threads || cli.show_all {
println!("Threads:");
// Grab the foreground tid.
let foreground_tid = dump.foreground_tid;
// Iterate through all the threads.
for (tid, thread) in dump.threads() {
// If the user specified a pid..
if let Some(wanted_tid) = cli.thread {
// .. skip an threads that don't match what the user wants..
if *tid != wanted_tid {
continue;
}
// Otherwise we keep going.
}
// If the user only wants the main thread, and we haven't found it,
// skip this thread until we find it.
if cli.show_foreground_thread
&& *tid != foreground_tid.expect("no foreground thread id in dump")
{
continue;
}
// Print out the thread info.
println!("TID {}, TEB {:016x}", tid, thread.teb);
println!("Context:");
println!("{}", thread.context());
}
}
// Do we want to dump memory?
if let Some(address) = cli.address {
println!("Memory:");
// Try to find a block that contains `address`.
let block = dump.get_mem_block(address);
// If we have one..
if let Some(block) = block {
// .. and it has data, dump it..
if let Some(data) = block.data_from(address) {
println!("{:016x} -> {:016x}", address, block.end_addr());
hexdump(address, data.iter().take(0x1_00).copied());
}
// .. otherwise, inform the user..
else {
println!(
"The memory at {:016x} (from block {:016x} -> {:016x}) has no backing data",
address, block.range.start, block.range.end
);
}
}
// .. otherwise, inform he user.
else {
println!("No memory block were found for {:016x}", address);
}
}
// All right, enough for today.
Ok(())
}