Skip to main content
Glama
test_data_access.py11.9 kB
"""End-to-end tests for data access operations""" import pytest from bridge_mcp_ghidra import ( get_data_by_address, set_data_type, create_struct, add_struct_field, query ) class TestDataByAddress: """Test get_data_by_address functionality""" def test_get_data_by_address_basic(self, ghidra_server): """Test getting data at a basic address""" # First get some data addresses from the listing result = query(type="data", limit=10) assert isinstance(result, list) if len(result) > 0: # Parse out an address from the first data item # Format is typically "address: name [type, size bytes] = value" first_line = result[0] if ":" in first_line: address = first_line.split(":")[0].strip() # Now test get_data_by_address data_result = get_data_by_address(address) assert isinstance(data_result, str) assert "Address:" in data_result assert "Type:" in data_result assert "Size:" in data_result def test_get_data_by_address_struct_member(self, ghidra_server): """Test getting data at an address inside a struct (the main fix)""" # Create a test struct struct_name = "TestDataStruct" create_struct(name=struct_name, size=16) # Add fields to the struct add_struct_field(struct_name=struct_name, field_type="byte", field_name="field0") add_struct_field(struct_name=struct_name, field_type="byte", field_name="field1") add_struct_field(struct_name=struct_name, field_type="byte", field_name="field2") add_struct_field(struct_name=struct_name, field_type="byte", field_name="field3") add_struct_field(struct_name=struct_name, field_type="int", field_name="field4") # Find an address where we can apply this struct # Get some data addresses to find a suitable location data_result = query(type="data", limit=50) assert isinstance(data_result, list) # Find an address we can use (look for undefined or small data) test_address = None for line in data_result: if ":" in line: addr = line.split(":")[0].strip() # Use the first data address we find if addr.startswith("0x") or addr.startswith("00"): test_address = addr break if test_address is None: pytest.skip("No suitable data address found for struct test") # Apply the struct type at this address set_result = set_data_type(address=test_address, type_name=struct_name) assert "success" in set_result.lower() or "Error" not in set_result # Now test get_data_by_address at the struct's start result = get_data_by_address(test_address) assert isinstance(result, str) assert "Address:" in result assert struct_name in result or "Type:" in result # Calculate an address inside the struct (offset +3 for field3) # Parse the hex address if test_address.startswith("0x"): base_addr = int(test_address, 16) else: base_addr = int(test_address, 16) member_address = f"0x{base_addr + 3:x}" # Test getting data at the struct member address (THIS IS THE FIX) member_result = get_data_by_address(member_address) assert isinstance(member_result, str) # Should NOT return an error anymore assert "Error: No data defined at address" not in member_result # Should contain information about the field assert "Address:" in member_result assert "Type:" in member_result assert "Size:" in member_result # Should show parent structure information assert "Parent" in member_result or "field" in member_result.lower() def test_get_data_by_address_array_element(self, ghidra_server): """Test getting data at an address inside an array""" # Get some data addresses data_result = query(type="data", limit=50) assert isinstance(data_result, list) # Find an address we can use test_address = None for line in data_result: if ":" in line: addr = line.split(":")[0].strip() if addr.startswith("0x") or addr.startswith("00"): test_address = addr break if test_address is None: pytest.skip("No suitable data address found for array test") # Apply an array type at this address set_result = set_data_type(address=test_address, type_name="byte[8]") assert "success" in set_result.lower() or "Error" not in set_result # Calculate an address inside the array (offset +4) if test_address.startswith("0x"): base_addr = int(test_address, 16) else: base_addr = int(test_address, 16) element_address = f"0x{base_addr + 4:x}" # Test getting data at the array element address element_result = get_data_by_address(element_address) assert isinstance(element_result, str) # Should NOT return an error assert "Error: No data defined at address" not in element_result # Should contain basic data info assert "Address:" in element_result assert "Type:" in element_result assert "Size:" in element_result def test_get_data_by_address_invalid_address(self, ghidra_server): """Test error handling for invalid addresses""" result = get_data_by_address("invalid_address") assert isinstance(result, str) assert "Error" in result or "Invalid" in result def test_get_data_by_address_no_data(self, ghidra_server): """Test error handling when no data exists at address""" # Use an address that's unlikely to have data result = get_data_by_address("0xffffffff") assert isinstance(result, str) # Should return an error since no program data exists there assert "Error" in result or "No data" in result class TestDataTypes: """Test set_data_type functionality""" def test_set_data_type_basic(self, ghidra_server): """Test setting a basic data type""" # Get a data address data_result = query(type="data", limit=10) assert isinstance(data_result, list) if len(data_result) > 0: first_line = data_result[0] if ":" in first_line: address = first_line.split(":")[0].strip() # Set a dword type result = set_data_type(address=address, type_name="dword") assert isinstance(result, str) # Verify the type was set verify_result = get_data_by_address(address) assert "dword" in verify_result.lower() or "4 bytes" in verify_result def test_set_data_type_pointer_no_space(self, ghidra_server): """Test setting a pointer type without space (e.g., 'int*')""" # Get a data address data_result = query(type="data", limit=10) assert isinstance(data_result, list) if len(data_result) > 0: first_line = data_result[0] if ":" in first_line: address = first_line.split(":")[0].strip() # Set a pointer type WITHOUT space - this was the bug result = set_data_type(address=address, type_name="int*") assert isinstance(result, str) assert "success" in result.lower(), f"Expected success but got: {result}" # Verify the type was set to a pointer, not int verify_result = get_data_by_address(address) # Should be a pointer (typically 4 or 8 bytes depending on arch) assert "int *" in verify_result or "pointer" in verify_result.lower() or "4 bytes" in verify_result or "8 bytes" in verify_result def test_set_data_type_pointer_with_space(self, ghidra_server): """Test setting a pointer type with space (e.g., 'int *')""" # Get a data address data_result = query(type="data", limit=10) assert isinstance(data_result, list) if len(data_result) > 0: first_line = data_result[0] if ":" in first_line: address = first_line.split(":")[0].strip() # Set a pointer type with space result = set_data_type(address=address, type_name="int *") assert isinstance(result, str) assert "success" in result.lower(), f"Expected success but got: {result}" # Verify the type was set to a pointer verify_result = get_data_by_address(address) assert "int *" in verify_result or "pointer" in verify_result.lower() or "4 bytes" in verify_result or "8 bytes" in verify_result def test_set_data_type_void_pointer(self, ghidra_server): """Test setting a void pointer type""" data_result = query(type="data", limit=10) assert isinstance(data_result, list) if len(data_result) > 0: first_line = data_result[0] if ":" in first_line: address = first_line.split(":")[0].strip() # Set void pointer - both variations for type_name in ["void*", "void *"]: result = set_data_type(address=address, type_name=type_name) assert isinstance(result, str) assert "success" in result.lower(), f"Expected success for '{type_name}' but got: {result}" def test_set_data_type_far_pointer(self, ghidra_server): """Test setting a far pointer type with explicit size (e.g., 'int *32')""" data_result = query(type="data", limit=10) assert isinstance(data_result, list) if len(data_result) > 0: first_line = data_result[0] if ":" in first_line: address = first_line.split(":")[0].strip() # Set a 32-bit far pointer result = set_data_type(address=address, type_name="int *32") assert isinstance(result, str) assert "success" in result.lower(), f"Expected success but got: {result}" # Verify - should be 4 bytes (32 bits) verify_result = get_data_by_address(address) assert "4 bytes" in verify_result or "pointer" in verify_result.lower() def test_set_data_type_custom_struct_pointer(self, ghidra_server): """Test setting a pointer to a custom struct""" # First create a custom struct struct_name = "TestPtrStruct" create_struct(name=struct_name, size=8) add_struct_field(struct_name=struct_name, field_type="int", field_name="value") # Get a data address data_result = query(type="data", limit=10) assert isinstance(data_result, list) if len(data_result) > 0: first_line = data_result[0] if ":" in first_line: address = first_line.split(":")[0].strip() # Set pointer to custom struct - without space (the bug case) result = set_data_type(address=address, type_name=f"{struct_name}*") assert isinstance(result, str) assert "success" in result.lower(), f"Expected success for '{struct_name}*' but got: {result}" # Also test with space result = set_data_type(address=address, type_name=f"{struct_name} *") assert isinstance(result, str) assert "success" in result.lower(), f"Expected success for '{struct_name} *' but got: {result}"

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/HK47196/GhidraMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server