Skip to content

Commit 0ac5f39

Browse files
authored
Merge pull request #7 from swiftly-solution/dev
Support finding networkvar statechanged vfunc
2 parents 95a6675 + f574fb2 commit 0ac5f39

File tree

5 files changed

+362
-9
lines changed

5 files changed

+362
-9
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
edition = "2024"
55

66
[lib]
7-
crate-type = ["staticlib", "dylib"]
7+
crate-type = ["staticlib"]
88

99
[profile.release]
1010
opt-level = 3

s2binlib.h

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,70 @@ extern "C"
389389
*/
390390
int s2binlib_find_vtable_nested_2(const char *binary_name, const char *class1_name, const char *class2_name, void **result);
391391

392+
/**
393+
* Get the number of virtual functions in a vtable
394+
*
395+
* Returns the count of virtual functions (vfuncs) in the specified vtable.
396+
* This counts valid function pointers in the vtable until it encounters a null
397+
* or invalid pointer.
398+
*
399+
* If the binary is not yet loaded, it will be loaded automatically.
400+
*
401+
* @param binary_name Name of the binary to search (e.g., "server", "client")
402+
* @param vtable_name Name of the vtable/class to search for
403+
* @param result Pointer to store the resulting count of virtual functions
404+
*
405+
* @return 0 - Success, result contains the vfunc count
406+
* -1 - S2BinLib not initialized
407+
* -2 - Invalid input (null pointer or invalid UTF-8)
408+
* -4 - Operation failed (vtable not found or other error)
409+
* -5 - Failed to acquire lock
410+
*
411+
* @example
412+
* size_t vfunc_count;
413+
* int result = s2binlib_get_vtable_vfunc_count("server", "CBaseEntity", &vfunc_count);
414+
* if (result == 0) {
415+
* printf("VTable has %zu virtual functions\n", vfunc_count);
416+
* }
417+
*/
418+
int s2binlib_get_vtable_vfunc_count(const char *binary_name, const char *vtable_name, size_t *result);
419+
420+
/**
421+
* Get the number of virtual functions in a vtable by virtual address
422+
*
423+
* Returns the count of virtual functions (vfuncs) in a vtable at the specified
424+
* virtual address. This counts valid function pointers in the vtable until it
425+
* encounters a null or invalid pointer.
426+
*
427+
* Unlike s2binlib_get_vtable_vfunc_count, this function takes a virtual address
428+
* directly instead of looking up the vtable by name.
429+
*
430+
* If the binary is not yet loaded, it will be loaded automatically.
431+
*
432+
* @param binary_name Name of the binary (e.g., "server", "client")
433+
* @param vtable_va Virtual address of the vtable
434+
* @param result Pointer to store the resulting count of virtual functions
435+
*
436+
* @return 0 - Success, result contains the vfunc count
437+
* -1 - S2BinLib not initialized
438+
* -2 - Invalid input (null pointer or invalid UTF-8)
439+
* -4 - Operation failed (invalid address or other error)
440+
* -5 - Failed to acquire lock
441+
*
442+
* @example
443+
* void* vtable_va;
444+
* // First get the vtable virtual address
445+
* s2binlib_find_vtable_va("server", "CBaseEntity", &vtable_va);
446+
*
447+
* // Then count its virtual functions
448+
* size_t vfunc_count;
449+
* int result = s2binlib_get_vtable_vfunc_count_by_va("server", (uint64_t)vtable_va, &vfunc_count);
450+
* if (result == 0) {
451+
* printf("VTable has %zu virtual functions\n", vfunc_count);
452+
* }
453+
*/
454+
int s2binlib_get_vtable_vfunc_count_by_va(const char *binary_name, uint64_t vtable_va, size_t *result);
455+
392456
/**
393457
* Find a symbol by name in the specified binary
394458
*
@@ -1123,6 +1187,59 @@ extern "C"
11231187
*/
11241188
int s2binlib_follow_xref_va_to_va(const char *binary_name, uint64_t va, uint64_t *target_va_out);
11251189

1190+
/**
1191+
* @brief Find the NetworkVar_StateChanged vtable index by virtual address
1192+
*
1193+
* This function scans the vtable at the given virtual address to find the
1194+
* index of the NetworkVar_StateChanged virtual function. It analyzes each
1195+
* virtual function in the vtable looking for the specific instruction pattern
1196+
* that identifies the StateChanged function (cmp dword ptr [reg+56], 0xFF).
1197+
*
1198+
* @param vtable_va Virtual address of the vtable to analyze
1199+
* @param result Pointer to store the resulting index (as uint64_t)
1200+
*
1201+
* @return 0 on success (index written to result)
1202+
* -1 if S2BinLib not initialized
1203+
* -2 if invalid parameters
1204+
* -4 if NetworkVar_StateChanged not found in vtable
1205+
* -5 if internal error
1206+
*
1207+
* @example
1208+
* uint64_t index;
1209+
* int result = s2binlib_find_networkvar_vtable_statechanged_va(0x140001000, &index);
1210+
* if (result == 0) {
1211+
* printf("StateChanged index: %llu\n", index);
1212+
* }
1213+
*/
1214+
int s2binlib_find_networkvar_vtable_statechanged_va(uint64_t vtable_va, uint64_t *result);
1215+
1216+
/**
1217+
* @brief Find the NetworkVar_StateChanged vtable index by memory address
1218+
*
1219+
* This function converts the runtime memory address to a virtual address,
1220+
* then scans the vtable to find the index of the NetworkVar_StateChanged
1221+
* virtual function. It analyzes each virtual function in the vtable looking
1222+
* for the specific instruction pattern that identifies the StateChanged function.
1223+
*
1224+
* @param vtable_mem_address Runtime memory address of the vtable
1225+
* @param result Pointer to store the resulting index (as uint64_t)
1226+
*
1227+
* @return 0 on success (index written to result)
1228+
* -1 if S2BinLib not initialized
1229+
* -2 if invalid parameters
1230+
* -3 if address conversion failed
1231+
* -4 if NetworkVar_StateChanged not found in vtable
1232+
* -5 if internal error
1233+
*
1234+
* @example
1235+
* uint64_t index;
1236+
* int result = s2binlib_find_networkvar_vtable_statechanged(vtable_ptr, &index);
1237+
* if (result == 0) {
1238+
* printf("StateChanged index: %llu\n", index);
1239+
* }
1240+
*/
1241+
int s2binlib_find_networkvar_vtable_statechanged(uint64_t vtable_mem_address, uint64_t *result);
1242+
11261243
#ifdef __cplusplus
11271244
}
11281245
#endif

src/c_bindings.rs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,86 @@ pub extern "C" fn s2binlib_get_vtable_vfunc_count(
12151215
}
12161216
}
12171217

1218+
/// Get the number of virtual functions in a vtable by virtual address
1219+
///
1220+
/// Returns the count of virtual functions (vfuncs) in a vtable at the specified
1221+
/// virtual address. This counts valid function pointers in the vtable until it
1222+
/// encounters a null or invalid pointer.
1223+
///
1224+
/// Unlike get_vtable_vfunc_count, this function takes a virtual address directly
1225+
/// instead of looking up the vtable by name.
1226+
///
1227+
/// If the binary is not yet loaded, it will be loaded automatically.
1228+
///
1229+
/// # Parameters
1230+
/// * `binary_name` - Name of the binary (e.g., "server", "client") (null-terminated C string)
1231+
/// * `vtable_va` - Virtual address of the vtable
1232+
/// * `result` - Pointer to store the resulting count of virtual functions
1233+
///
1234+
/// # Returns
1235+
/// * `0` - Success, result contains the vfunc count
1236+
/// * `-1` - S2BinLib not initialized
1237+
/// * `-2` - Invalid input (null pointer or invalid UTF-8)
1238+
/// * `-4` - Operation failed (invalid address or other error)
1239+
/// * `-5` - Failed to acquire lock
1240+
///
1241+
/// # Safety
1242+
/// This function is unsafe because it dereferences raw pointers.
1243+
/// The caller must ensure that the pointers are valid.
1244+
///
1245+
/// # Example
1246+
/// ```c
1247+
/// void* vtable_va;
1248+
/// // First get the vtable virtual address
1249+
/// s2binlib_find_vtable_va("server", "CBaseEntity", &vtable_va);
1250+
///
1251+
/// // Then count its virtual functions
1252+
/// size_t vfunc_count;
1253+
/// int result = s2binlib_get_vtable_vfunc_count_by_va("server", (uint64_t)vtable_va, &vfunc_count);
1254+
/// if (result == 0) {
1255+
/// printf("VTable has %zu virtual functions\n", vfunc_count);
1256+
/// }
1257+
/// ```
1258+
#[unsafe(no_mangle)]
1259+
pub extern "C" fn s2binlib_get_vtable_vfunc_count_by_va(
1260+
binary_name: *const c_char,
1261+
vtable_va: u64,
1262+
result: *mut usize,
1263+
) -> i32 {
1264+
unsafe {
1265+
if binary_name.is_null() || result.is_null() {
1266+
return -2;
1267+
}
1268+
1269+
let binary_name_str = match CStr::from_ptr(binary_name).to_str() {
1270+
Ok(s) => s,
1271+
Err(_) => return -2,
1272+
};
1273+
1274+
let mut s2binlib_guard = match S2BINLIB.lock() {
1275+
Ok(guard) => guard,
1276+
Err(_) => return -5,
1277+
};
1278+
1279+
let s2binlib = match s2binlib_guard.as_mut() {
1280+
Some(lib) => lib,
1281+
None => return -1,
1282+
};
1283+
1284+
if !s2binlib.is_binary_loaded(binary_name_str) {
1285+
s2binlib.load_binary(binary_name_str);
1286+
}
1287+
1288+
match s2binlib.get_vtable_vfunc_count_by_va(binary_name_str, vtable_va) {
1289+
Ok(count) => {
1290+
*result = count;
1291+
0
1292+
}
1293+
Err(_) => -4,
1294+
}
1295+
}
1296+
}
1297+
12181298
/// Pattern scan and return the virtual address
12191299
///
12201300
/// Scans for a byte pattern in the specified binary and returns the virtual address (VA)
@@ -2961,3 +3041,122 @@ pub extern "C" fn s2binlib_follow_xref_va_to_va(
29613041
}
29623042
}
29633043

3044+
/// Find the NetworkVar_StateChanged vtable index by virtual address
3045+
///
3046+
/// This function scans the vtable at the given virtual address to find the
3047+
/// index of the NetworkVar_StateChanged virtual function. It analyzes each
3048+
/// virtual function in the vtable looking for the specific instruction pattern
3049+
/// that identifies the StateChanged function (cmp dword ptr [reg+56], 0xFF).
3050+
///
3051+
/// # Parameters
3052+
/// * `vtable_va` - Virtual address of the vtable to analyze
3053+
/// * `result` - Pointer to store the resulting index (as u64)
3054+
///
3055+
/// # Returns
3056+
/// * 0 on success (index written to result)
3057+
/// * -1 if S2BinLib not initialized
3058+
/// * -2 if invalid parameters
3059+
/// * -4 if NetworkVar_StateChanged not found in vtable
3060+
/// * -5 if internal error
3061+
///
3062+
/// # Safety
3063+
/// This function is unsafe because it dereferences raw pointers.
3064+
///
3065+
/// # Example
3066+
/// ```c
3067+
/// uint64_t index;
3068+
/// int result = s2binlib_find_networkvar_vtable_statechanged_va(0x140001000, &index);
3069+
/// if (result == 0) {
3070+
/// printf("StateChanged index: %llu\n", index);
3071+
/// }
3072+
/// ```
3073+
#[unsafe(no_mangle)]
3074+
pub extern "C" fn s2binlib_find_networkvar_vtable_statechanged_va(
3075+
vtable_va: u64,
3076+
result: *mut u64,
3077+
) -> i32 {
3078+
unsafe {
3079+
if result.is_null() {
3080+
return -2;
3081+
}
3082+
3083+
let s2binlib_guard = match S2BINLIB.lock() {
3084+
Ok(guard) => guard,
3085+
Err(_) => return -5,
3086+
};
3087+
3088+
let s2binlib = match s2binlib_guard.as_ref() {
3089+
Some(lib) => lib,
3090+
None => return -1,
3091+
};
3092+
3093+
match s2binlib.find_networkvar_vtable_statechanged_va(vtable_va) {
3094+
Ok(index) => {
3095+
*result = index;
3096+
0
3097+
}
3098+
Err(_) => -4,
3099+
}
3100+
}
3101+
}
3102+
3103+
/// Find the NetworkVar_StateChanged vtable index by memory address
3104+
///
3105+
/// This function converts the runtime memory address to a virtual address,
3106+
/// then scans the vtable to find the index of the NetworkVar_StateChanged
3107+
/// virtual function. It analyzes each virtual function in the vtable looking
3108+
/// for the specific instruction pattern that identifies the StateChanged function.
3109+
///
3110+
/// # Parameters
3111+
/// * `vtable_mem_address` - Runtime memory address of the vtable
3112+
/// * `result` - Pointer to store the resulting index (as u64)
3113+
///
3114+
/// # Returns
3115+
/// * 0 on success (index written to result)
3116+
/// * -1 if S2BinLib not initialized
3117+
/// * -2 if invalid parameters
3118+
/// * -3 if address conversion failed
3119+
/// * -4 if NetworkVar_StateChanged not found in vtable
3120+
/// * -5 if internal error
3121+
///
3122+
/// # Safety
3123+
/// This function is unsafe because it dereferences raw pointers.
3124+
///
3125+
/// # Example
3126+
/// ```c
3127+
/// uint64_t index;
3128+
/// int result = s2binlib_find_networkvar_vtable_statechanged(vtable_ptr, &index);
3129+
/// if (result == 0) {
3130+
/// printf("StateChanged index: %llu\n", index);
3131+
/// }
3132+
/// ```
3133+
#[unsafe(no_mangle)]
3134+
pub extern "C" fn s2binlib_find_networkvar_vtable_statechanged(
3135+
vtable_mem_address: u64,
3136+
result: *mut u64,
3137+
) -> i32 {
3138+
unsafe {
3139+
if result.is_null() {
3140+
return -2;
3141+
}
3142+
3143+
let s2binlib_guard = match S2BINLIB.lock() {
3144+
Ok(guard) => guard,
3145+
Err(_) => return -5,
3146+
};
3147+
3148+
let s2binlib = match s2binlib_guard.as_ref() {
3149+
Some(lib) => lib,
3150+
None => return -1,
3151+
};
3152+
3153+
match s2binlib.find_networkvar_vtable_statechanged(vtable_mem_address) {
3154+
Ok(index) => {
3155+
*result = index;
3156+
0
3157+
}
3158+
Err(_) => -4,
3159+
}
3160+
}
3161+
}
3162+

src/lib.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,13 @@ mod tests {
3333
use std::time::Instant;
3434

3535
use anyhow::Result;
36+
use iced_x86::{Code, Decoder, DecoderOptions, Mnemonic, OpKind};
3637

3738
use super::*;
3839

3940
#[test]
4041
fn test_s2binlib() -> Result<()> {
41-
let mut s2binlib = S2BinLib::new("F:/cs2server/game", "csgo", "windows");
42+
let mut s2binlib = S2BinLib::new("F:/cs2server/game", "csgo", "linux");
4243

4344
s2binlib.load_binary("server");
4445

@@ -47,14 +48,14 @@ mod tests {
4748

4849
// s2binlib.load_binary("tier0");
4950
// println!("1");
50-
// println!("{:X}", s2binlib.find_vtable_va("server", "CTraceFilter")?);
51+
5152

5253
let start = Instant::now();
5354

54-
let xref = s2binlib.pattern_scan_va("server", "4C 8D 35 ? ? ? ? 77")?;
55+
// let xref = s2binlib.pattern_scan_va("server", "4C 8D 35 ? ? ? ? 77")?;
5556

56-
let duration = start.elapsed();
57-
println!("Time taken: {:?}", duration);
57+
// let duration = start.elapsed();
58+
// println!("Time taken: {:?}", duration);
5859
// println!("xref {:X}", xref);
5960
// println!("follow xref {:X}", s2binlib.follow_xref_va_to_va("server", xref)?);
6061

0 commit comments

Comments
 (0)