commit 181a3b0fd303d360a37b624a37352393b9c6b7ae Author: Amelia Deck Date: Fri May 31 06:05:23 2024 -0500 Working test api (wifi mode), firmware download, key extractor diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0b8ff49 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.bin +firmware/ +firmware/* +*.csv +listing.txt +*.pyc +__pycache__ \ No newline at end of file diff --git a/call_api.py b/call_api.py new file mode 100644 index 0000000..c2f4e64 --- /dev/null +++ b/call_api.py @@ -0,0 +1,24 @@ +import requests + +def call_api(api_url, method, params): + """ + Function to make an API call. + + Parameters: + - api_url (str): The URL of the API endpoint. + - method (str): The HTTP method to use ('POST' or 'GET'). + - params (dict): Dictionary of parameters. + + Returns: + - response: The response from the API call. + """ + + print("Calling API",api_url,"with method",method,"and parameters",params) + if method.upper() == 'POST': + response = requests.post(api_url, data=params, verify=False) + elif method.upper() == 'GET': + response = requests.get(api_url, params=params, verify=False) + else: + raise ValueError("Method must be 'POST' or 'GET'") + + return response diff --git a/cleanup.sh b/cleanup.sh new file mode 100644 index 0000000..d07ab00 --- /dev/null +++ b/cleanup.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +rm -rfv firmware *.bin* fwname listing.txt \ No newline at end of file diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..56328e9 --- /dev/null +++ b/config.yml @@ -0,0 +1,2 @@ +tool_directory: ./firmware/www/tool/ +app_config_directory: . diff --git a/download-fw.sh b/download-fw.sh new file mode 100644 index 0000000..72575a0 --- /dev/null +++ b/download-fw.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Firmware downloader + +DEVICE=NE1D +SERIAL=1133001 +VERSION=194 + +BASEURL=https://updates.netool.io/bin/ + + +wget -O listing.txt $BASEURL + +filename=$DEVICE-$VERSION-UP-$SERIAL.bin +rm *.bin* +if cat listing.txt | grep $filename && wget $BASEURL$filename; then + # exact version match + echo $filename > fwname +else + filename=$DEVICE-$VERSION-UP-1133333.bin + if cat listing.txt | grep $filename && wget $BASEURL$filename; then + echo $filename > fwname + else + echo "ERROR: Unable to find firmware!" + rm fwname + fi +fi diff --git a/extract.sh b/extract.sh new file mode 100644 index 0000000..7825102 --- /dev/null +++ b/extract.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# Define the paths to the SquashFS images +IMAGE="$1" + +echo $IMAGE + +# Define the mount points +MOUNTPOINT="./firmware" + +# Create the mount points if they don't exist +mkdir -p $MOUNTPOINT + +# Mount the SquashFS images +7z x -o$MOUNTPOINT $IMAGE + +# Perform the diff and show only the differences +#diff --no-dereference -r $MOUNTPOINT + +# Remove the mount points +#rm -rf $MOUNTPOINT diff --git a/get_codes.py b/get_codes.py new file mode 100644 index 0000000..884e6d4 --- /dev/null +++ b/get_codes.py @@ -0,0 +1,94 @@ +import yaml +import os +import re + +def read_php_files(input, output): + # Load the directory path from the YAML file + + search_list=['sec', 'code', 'id', 'post_key', 'pass', 'key'] # why is there so many? + param_list=['POST','GET','DELETE','PUT'] # so far looks like only POST and GET, include the others just in case + directory_path = input + + # Check if the directory exists + if not os.path.isdir(directory_path): + print(f"The directory {directory_path} does not exist.") + return + + # List all .php files in the directory + php_files = [f for f in os.listdir(directory_path) if f.endswith('.php')] + if not php_files: + print("No PHP files found in the directory.") + return + + out = "" + # Process each .php file + for filename in php_files: + file_path = os.path.join(directory_path, filename) + with open(file_path, 'r', encoding='utf-8') as file: + contents = file.read() + add = filename + found_params = [] + for paramtype in param_list: + for match in re.finditer("_" + paramtype, contents): + start_line = contents.rfind('\n', 0, match.start()) + 1 + end_line = contents.find('\n', match.end(), -1) + line = contents[start_line:end_line] + if "[" in line and "]" in line: + quoted_strings = re.findall(r'["\'](.*?)["\']', line) + if len(quoted_strings) == 1: + found_params.append((quoted_strings[0], line[line.find("$")+1:line.find('=')].strip())) + add += ",PARAM:," + quoted_strings[0] + "," + paramtype + + for codetype in search_list: + for match in re.finditer("\$" + codetype, contents): + # Extract the line containing the matched string + start_line = contents.rfind('\n', 0, match.start()) + 1 + end_line = contents.find('\n', match.end(), -1) + line = contents[start_line:end_line] + + if '==' in line and not "POST" in line and not "GET" in line: + #print(line) + quoted_strings = re.findall(r'["\'](.*?)["\']', line) + if len(quoted_strings) == 1: + if codetype not in [x[0] for x in found_params]: + # non-matching variable & key! + found = False + for val in found_params: + if codetype == val[1]: + found = True + codetype2 = val[0] + print("NOTE: Alternate parameter variable used!", codetype, "-->", codetype2, "in file", filename) + add += ",KEY:," + codetype2 + "," + quoted_strings[0] + break + if not found: + print("WARNING: No matching parameter variable found!" , codetype, "--> ??? in file", filename) + add += ",KEY:," + codetype + "," + quoted_strings[0] + + else: + add += ",KEY:," + codetype + "," + quoted_strings[0] + + + + if add == filename: + out += add + ",null" + else: + out += add + out += "\n" + + print(out) + + with open(output + "/apidetails.csv", 'w', encoding='utf-8') as file: + # Write the string to the file + file.write(out) + + + +# Example usage + + +if __name__ == "__main__": + with open('config.yml', 'r') as file: + config = yaml.safe_load(file) + directory_path = config['tool_directory'] + output_path = config['app_config_directory'] + read_php_files(directory_path, output_path) diff --git a/interactive_api.py b/interactive_api.py new file mode 100644 index 0000000..450b899 --- /dev/null +++ b/interactive_api.py @@ -0,0 +1,52 @@ +from call_api import call_api +from read_api_details import parse_csv_to_dict +import json + +def main(): + base_url = "https://192.168.49.1/tool/" # Replace with your actual base URL + details = parse_csv_to_dict("apidetails.csv") + print(details.keys()) + # Prompt the user for the API call + api_call = input("Enter the API call (e.g., about.php): ").strip() + full_url = base_url + api_call + print(details[api_call]) + # Prompt the user for the method + method = input("Enter the HTTP method (POST/GET): ").strip().upper() + auto_include = [] + if 'PARAM' in details[api_call]: + for param, val in details[api_call]["PARAM"].items(): + # print(param, val) + if val == method: + print(param, end="") + if 'KEY' in details[api_call]: + for keyname, key in details[api_call]["KEY"].items(): + if keyname == param: + print("=" + key, end="") + auto_include.append((keyname, key)) + print("") + + + # Prompt the user for parameters + params = {} + while True: + param_name = input("Enter parameter name (or press enter to finish): ").strip() + if param_name == "": + break + param_value = input(f"Enter value for '{param_name}': ").strip() + params[param_name] = param_value + + for param in auto_include: + params[param[0]] = param[1] + # Call the API + try: + response = call_api(full_url, method, params) + print("Response Status Code:", response.status_code) + try: + print("Response JSON:", json.dumps(response.json(), indent=2)) + except ValueError: + print("Response Text:", response.text) + except Exception as e: + print("Error:", str(e)) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/read_api_details.py b/read_api_details.py new file mode 100644 index 0000000..8bc60e8 --- /dev/null +++ b/read_api_details.py @@ -0,0 +1,55 @@ +import csv + +def parse_csv_to_dict(csv_file_path): + """ + Parses a CSV file into a dictionary. + + Parameters: + - csv_file_path (str): The path to the CSV file. + + Returns: + - dict: A dictionary representation of the CSV file. + """ + parsed_dict = {} + + with open(csv_file_path, mode='r', newline='', encoding='utf-8') as csvfile: + reader = csv.reader(csvfile) + for row in reader: + if len(row) < 2: + continue # Skip rows that do not have at least two columns + name = row[0].strip() + if name not in parsed_dict: + parsed_dict[name] = {} + + index = 1 + while index < len(row): + field_type = row[index].strip() if len(row) > index else None + #print(field_type, end=" ") + if not field_type: + break # End of relevant columns for this row + + if field_type in ('KEY:', 'PARAM:') and len(row) > index + 2: + key_or_param_name = row[index + 1].strip() + key_or_param_value = row[index + 2].strip() + + if field_type == 'KEY:': + if 'KEY' not in parsed_dict[name]: + parsed_dict[name]['KEY'] = {} + parsed_dict[name]['KEY'][key_or_param_name] = key_or_param_value + elif field_type == 'PARAM:': + if 'PARAM' not in parsed_dict[name]: + parsed_dict[name]['PARAM'] = {} + parsed_dict[name]['PARAM'][key_or_param_name] = key_or_param_value + + index += 3 + #print("") + # If no valid field_type was found, set the entry to None + if name not in parsed_dict: + parsed_dict[name] = None + + return parsed_dict + +if __name__ == "__main__": + csv_file_path = 'apidetails.csv' + result = parse_csv_to_dict(csv_file_path) + print(result) diff --git a/setup-build.sh b/setup-build.sh new file mode 100644 index 0000000..e2efe2d --- /dev/null +++ b/setup-build.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +if ! [ -e apidetails.csv ]; then + + ./download-fw.sh + ./extract.sh $(cat fwname) + DIR=./firmware/www/tool/ + + echo "tool_directory: $DIR +app_config_directory: ." > config.yml + + python get_codes.py + + ./cleanup.sh +fi \ No newline at end of file