commit d053ce0fc0d6de18cde588b821a06b23b6b57019 Author: xchgll Date: Tue Jul 7 07:42:44 2026 +0300 Upload diff --git a/README.md b/README.md new file mode 100644 index 0000000..8b447f2 --- /dev/null +++ b/README.md @@ -0,0 +1,126 @@ +# WinMan + +

+ Windows Man Pages + +

+ +

+ Windows Man Pages + Windows Man Pages + Windows Man Pages +

+ + +**WinMan** is an offline documentation for WIN/NT API with a man pages taste. + +the tool downloads the documentation only one time, then you can browse it fully offline. + +--- + +## Features + +- its fully offline +- easy - fast searching & support wildcards `Virtual*` +- it supports windows/linux/macosX +- documentation is monthly updated + + +## Installation + +only run + +```bash +pip install winman==1.0.1 +``` + +- Note: on windows do not forget to add Python Scripts Path to your environment variables + +run `winman -u` to download or update documentations for first time + +```bash +(venv) kali@kali:~$ winman -u +[?] Seems first time use winman +Download Documentation ? (y/n) +y +[+] New Update: First Version +Version: 1 +Download Size: 14.8 MB +Disk Size: 112 MB +Wanna Update ? (y/n) +y +[#] Please Wait... +[#] Extracting Data +_misc.json +a.json +b.json +... +``` + +``` +(venv) kali@kali:~$ winman -q VirtualProtect +VirtualProtect + VirtualProtect function (memoryapi.h) + +SYNOPSIS + Header : memoryapi.h + DLL : Kernel32.dll + LIB : onecore.lib + API : VirtualProtect + +DESCRIPTION + Changes the protection on a region of committed pages in the virtual address space of the calling process. + +To change the access protection of any process, use the [VirtualProtectEx](/windows/win32/api/memoryapi/nf-memoryapi-virtualprotectex) function. + +PARAMETERS + lpAddress + Direction : in + The address of the starting page of the region of pages whose access protection attributes are to be changed. + +All pages in the specified region must be within the same reserved region allocated when calling the [VirtualAlloc](/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc) or [VirtualAllocEx](/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex) function using **MEM_RESERVE**. The pages cannot span adjacent reserved regions that were allocated by separate calls to **VirtualAlloc** or **VirtualAllocEx** using **MEM_RESERVE**. + + dwSize + Direction : in + The size of the region whose access protection attributes are to be changed, in bytes. The region of affected pages includes all pages containing one or more bytes in the range from the ***lpAddress*** parameter to `(lpAddress+dwSize)`. This means that a 2-byte range straddling a page boundary causes the protection attributes of both pages to be changed. + + flNewProtect + Direction : in + The memory protection option. This parameter can be one of the [memory protection constants](/windows/win32/Memory/memory-protection-constants). + +For mapped views, this value must be compatible with the access protection specified when the view was mapped (see [MapViewOfFile](/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile), [MapViewOfFileEx](/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffileex), and [MapViewOfFileExNuma](/windows/win32/api/winbase/nf-winbase-mapviewoffileexnuma)). + + lpflOldProtect + Direction : out + A pointer to a variable that receives the previous access protection value of the first page in the specified region of pages. If this parameter is **NULL** or does not point to a valid variable, the function fails. + +RETURN VALUE + If the function succeeds, the return value is nonzero. + +If the function fails, the return value is zero. To get extended error information, call [GetLastError](/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror). + +REMARKS +You can set the access protection value on committed pages only. If the state of any page in the specified region is not committed, the function fails and returns without modifying the access protection of any pages in the specified region. + +The **PAGE_GUARD** protection modifier establishes guard pages. Guard pages act as one-shot access alarms. For more information, see [Creating Guard Pages](/windows/win32/Memory/creating-guard-pages). + +It is best to avoid using **VirtualProtect** to change page protections on memory blocks allocated by [GlobalAlloc](/windows/win32/api/winbase/nf-winbase-globalalloc), [HeapAlloc](/windows/win32/api/heapapi/nf-heapapi-heapalloc), or [LocalAlloc](/windows/win32/api/winbase/nf-winbase-localalloc), because multiple memory blocks can exist on a single page. The heap manager assumes that all pages in the heap grant at least read and write access. + +When protecting a region that will be executable, the calling program bears responsibility for ensuring cache coherency via an appropriate call to [FlushInstructionCache](/windows/win32/api/processthreadsapi/nf-processthreadsapi-flushinstructioncache) once the code has been set in place. Otherwise attempts to execute code out of the newly executable region may produce unpredictable results. + +REQUIREMENTS + Client : Windows XP [desktop apps \| UWP apps] + Server : Windows Server 2003 [desktop apps \| UWP apps] + +SOURCE + memoryapi/nf-memoryapi-virtualprotect.md + +==================== + +``` + +You can also use wildcards + +``` +winman -q Virtual* +``` \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..97a1112 --- /dev/null +++ b/config.json @@ -0,0 +1,6 @@ +{ + "note":"First Version", + "version":1, + "zip_size":"14.8 MB", + "disk_size":"112 MB" +} \ No newline at end of file diff --git a/images/win-man-pages-logo.png b/images/win-man-pages-logo.png new file mode 100644 index 0000000..907f05f Binary files /dev/null and b/images/win-man-pages-logo.png differ diff --git a/src/pyproject.toml b/src/pyproject.toml new file mode 100644 index 0000000..96d6e9d --- /dev/null +++ b/src/pyproject.toml @@ -0,0 +1,15 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "winman" +version = "1.0.1" +authors = [ + { name="xchgll", email="no@email.com" } +] +description = "WinMan is an offline documentation for WIN/NT API with a man pages taste." +requires-python = ">=3.8" + +[project.scripts] +winman = "winman:main" \ No newline at end of file diff --git a/src/winman.py b/src/winman.py new file mode 100644 index 0000000..86164ad --- /dev/null +++ b/src/winman.py @@ -0,0 +1,266 @@ +# Author: @xchgll + +import requests +import argparse +import zipfile +import json +import os +from fnmatch import fnmatch + +CONFIG_DIR = "" +CONFIG_FILE = "" + +WHITE = "\033[37m" +BOLD = "\033[1m" +RESET = "\033[0m" + +if os.name == "nt": + CONFIG_DIR = os.path.join(os.environ.get("USERPROFILE"),".winman") +else: + CONFIG_DIR = os.path.join(os.environ.get("HOME"),".winman") + +CONFIG_FILE = os.path.join(CONFIG_DIR,"config.json") +DOCN_DIR = os.path.join(CONFIG_DIR,"docn") + +REPO_BASE = "https://raw.githubusercontent.com/xchgll/winman/refs/heads/master/" +REPO_CONFIG = REPO_BASE + "config.json" +REPO_DOCN = REPO_BASE + "winapi_docs_json.zip" + +def update_docn(): + + if not os.path.exists(DOCN_DIR): + os.makedirs(DOCN_DIR,exist_ok=True) + + print("[#] Please Wait...") + + download = requests.get(REPO_DOCN) + + with open("download_temp.zip","wb") as f: + f.write(download.content) + + print("[#] Extracting Data") + + with zipfile.ZipFile("download_temp.zip") as zf: + for member in zf.infolist(): + print(member.filename) + if member.is_dir(): + continue + filename = os.path.basename(member.filename) + if not filename: + continue + target = os.path.join(DOCN_DIR, filename) + with zf.open(member) as src, open(target, "wb") as dst: + dst.write(src.read()) + + config_resp = requests.get(REPO_CONFIG) + + with open(CONFIG_FILE,"w") as conf: + conf.write(config_resp.text) + + os.remove("download_temp.zip") + + print("[+] Done") + +def check_version(): + try: + resp = requests.get(REPO_CONFIG) + + repo_version = resp.json() + + if resp.status_code in (400,403,404): + print("[!] Error fetching repositry: ",resp.status_code) + return + except: + print("[!] Cannot Check Version... Skipping") + + try: + with open(CONFIG_FILE,"r") as f: + current_version = json.load(f) + + # first time update + except FileNotFoundError: + print(BOLD + WHITE + "[?] Seems first time use winman\nDownload Documentation ? (y/n)" + RESET) + value = input().lower() + + if value[0] == "n": + return + + # to force update + current_version = {"version": 0} + + + # compare + try: + if repo_version["version"] > current_version["version"]: + print(BOLD + WHITE + "[+] New Update: %s" % repo_version.get("note","")) + + print( + ("Version: %d\nDownload Size: %s\nDisk Size: %s" + RESET) % ( + repo_version["version"], + repo_version["zip_size"], + repo_version["disk_size"], + ) + ) + + + print("Wanna Update ? (y/n)") + + value = input().lower() + if value[0] == "n": + return + else: + update_docn() + return + + else: + print("[+] Up to Date") + except Exception as e: + print("[!] Error while checking version " + str(e)) + + +def dump_entry(entry): + def h(title): + print(f"{BOLD}{WHITE}{title}{RESET}") + + h(entry["name"]) + + if entry.get("title"): + print(f" {entry['title']}") + + print() + + h("SYNOPSIS") + + if entry.get("header"): + print(f" Header : {entry['header']}") + + if entry.get("dll"): + print(f" DLL : {entry['dll']}") + + if entry.get("lib"): + print(f" LIB : {entry['lib']}") + + if entry.get("api_names"): + print(f" API : {', '.join(entry['api_names'])}") + + print() + + if entry.get("description"): + h("DESCRIPTION") + print(f" {entry['description']}") + print() + + params = entry.get("parameters", []) + if params: + h("PARAMETERS") + + for p in params: + print(f" {BOLD}{p['name']}{RESET}") + + if p.get("direction"): + print(f" Direction : {p['direction']}") + + if p.get("description"): + print(f" {p['description']}") + + print() + + if entry.get("return_value"): + h("RETURN VALUE") + print(f" {entry['return_value']}") + print() + + if entry.get("remarks"): + h("REMARKS") + print(entry["remarks"]) + print() + + if entry.get("min_supported_client") or entry.get("min_supported_server"): + h("REQUIREMENTS") + + if entry.get("min_supported_client"): + print(f" Client : {entry['min_supported_client']}") + + if entry.get("min_supported_server"): + print(f" Server : {entry['min_supported_server']}") + + print() + + if entry.get("unicode_ansi"): + h("CHARACTER SET") + print(f" {entry['unicode_ansi']}") + print() + + see_also = entry.get("see_also", []) + + if see_also: + h("SEE ALSO") + + for item in see_also: + print(f" {BOLD + WHITE + item.get("text","") + RESET}\thttps://learn.microsoft.com/en-us{item.get("url","")}") + + print() + + if entry.get("source_file"): + h("SOURCE") + print(f" {entry['source_file']}") + + h(f"\n{"="*20}\n") + +def query_function(func: str): + + search_c = func.lower()[0] + + if search_c.isalpha(): + search_c = search_c + else: + search_c = "_misc" + + try: + + with open(os.path.join(DOCN_DIR,search_c+".json"),"r",encoding="utf-8") as df: + data = json.load(df) + for winfunc in data: + if fnmatch(winfunc["name"],func): + dump_entry(winfunc) + + except FileNotFoundError: + print("[#] Database not found... please try run winman -u to update") + + +def main(): + + parser = argparse.ArgumentParser( + prog="WinMan", + description="Offline documentation browser for Windows and NT APIs" + ) + + parser.add_argument( + "-q", "--query", + required=False, + help="API name or pattern to search for" + ) + + parser.add_argument( + "-u", "--update", + required=False, + action="store_true", + help="Update Check" + ) + + + args = parser.parse_args() + + if args.update: + check_version() + + query = args.query + + if not query: + exit(0) + + + query_function(query) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/winapi_docs_json.zip b/winapi_docs_json.zip new file mode 100644 index 0000000..82e080d Binary files /dev/null and b/winapi_docs_json.zip differ