fa


Namefa JSON
Version 1.0.8 PyPI version JSON
download
home_pageNone
SummaryAutomation tool for locating symbols & structs in binary (primarily IDA focused)
upload_time2024-12-08 15:41:46
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseGNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007
keywords reverse-engineering ida automation signatures symbols
VCS
bugtrack_url
requirements keystone-engine capstone click hjson future configparser six rpyc click ipython termcolor
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ![Python application](https://github.com/doronz88/fa/workflows/Python%20application/badge.svg)

# FA

## What is it?

FA stands for Firmware Analysis and intended **For Humans**.

FA allows one to easily perform code exploration, symbol searching and 
other functionality with ease.

Usually such tasks would require you to understand complicated APIs,
write machine-dependant code and perform other tedious tasks.
FA is meant to replace the steps one usually performs like a robot 
(find X string, goto xref, find the next call function, ...) in 
a much friendlier and maintainable manner. 

The current codebase is very IDA-plugin-oriented. In the future I'll
consider adding compatibility for other disassemblers such as:
Ghidra, Radare and etc...


Pull Requests are of course more than welcome :smirk:.

## Requirements

Supported IDA 8.0+.

In your IDA's python directory, run:

```sh
python -m pip install -r requirements.txt
```

And for testing:
```sh
python -m pip install -r requirements_testing.txt
```

## Where to start?

Before using, you should understand the terminology for: 
Projects, SIG files and Loaders.

So, grab a cup of coffee, listen to some [nice music](https://www.youtube.com/watch?v=5rrIx7yrxwQ), and please devote 
a few minutes of your time into reading this README.

### Projects

The "project" is kind of the namespace for different signatures.
For example, either: linux, linux_x86, linux_arm etc... are good 
project names that can be specified if you are working on either 
platforms. 

By dividing the signatures into such projects, Windows symbols for 
example won't be searched for Linux projects, which will result 
in a better directory organization layout, better performance and
less rate for false-positives. 

The signatures are located by default in the `signatures` directory.
If you wish to use a different location, you may create `config.ini`
at FA's root with the following contents:

```ini
[global]
signatures_root = /a/b/c
```

### SIG format

The SIG format is a core feature of FA regarding symbol searching. Each
SIG file is residing within the project directory and is automatically searched
when requested to generate the project's symbol list.

The format is Hjson-based and is used to describe what you, 
**as a human**, would do in order to perform the given task (symbol searching
or binary exploration).

SIG syntax (single):
```hjson
{
    name: name  
    instructions : [
        # Available commands are listed below
        command1
        command2
    ]
}
```

Each line in the `instructions` list behaves like a shell
command-line that gets the previous results as the input 
and outputs the next results
to the next line.

Confused? That's alright :grinning:. [Just look at the examples below](#examples)

User may also use his own python script files to perform 
the search. Just create a new `.py` file in your project 
directory and implement the `run(interpreter)` method. 
Also, the project's path is appended to python's `sys.path` 
so you may import your scripts from one another.

To view the list of available commands, [view the list below](#available-commands)

### Examples

#### Finding a global struct

```hjson
{
    name: g_awsome_global,
    instructions: [
            # find the byte sequence '11 22 33 44'
            find-bytes '11 22 33 44'

            # advance offset by 20
            offset 20

            # verify the current bytes are 'aa bb cc dd'
            verify-bytes 'aa bb cc dd'

            # go back by 20 bytes offset
            offset -20

            # set global name
            set-name g_awsome_global
	]
}
```

#### Find function by reference to string

```hjson
{
    name: free
    instructions: [
            # search the string "free"
            find-str 'free' --null-terminated

            # goto xref
            xref

            # goto function's prolog
            function-start

            # reduce to the singletone with most xrefs to
            max-xrefs

            # set name and type
            set-name free
            set-type 'void free(void *block)'
	]
}
```

#### Performing code exploration

```hjson
{
    name: arm-explorer
    instructions: [
            # search for some potential function prologs
            arm-find-all 'push {r4, lr}'
            arm-find-all 'push {r4, r5, lr}'
            thumb-find-all 'push {r4, lr}'
            thumb-find-all 'push {r4, r5, lr}'

            # convert into functions
            make-function
	]
}
```

#### Performing string exploration

```hjson
{
    name: arm-string-explorer
    instructions: [
            # goto printf
            locate printf

            # iterate every xref
            xref

            # and for each, go word-word backwards
            add-offset-range 0 -40 -4

            # if ldr to r0
            verify-operand ldr --op0 r0

            # go to the global string
            goto-ref --data

            # and make it literal
            make-literal
	]
}
```

#### Finding enums and constants 

```hjson
{
    name: consts-finder
    instructions: [
            # goto printf
            locate printf

            # iterate all its function lines
            function-lines

            # save this result
            store printf-lines
            
            # look for: li r7, ???
            verify-operand li --op0 7

            # extract second operand
            operand 1

            # define the constant
            set-const IMPORTANT_OFFSET

            # load previous results
            load printf-lines
            
            # look for: li r7, ???
            verify-operand li --op0 8

            # get second operand
            operand 1

            # set this enum value
            set-enum important_enum_t some_enum_key
	]
}
```

#### Adding struct member offsets 

```hjson
{
    name: structs-finder
    instructions: [
            # add hard-coded '0' into resultset
            add 0

            # add a first member at offset 0
            set-struct-member struct_t member_at_0 'unsigned int'

            # advance offset by 4
            offset 4            

            # add a second member
            set-struct-member struct_t member_at_4 'const char *'

            # goto function printf
            locate printf

            # iterate its function lines 
            function-lines

            # look for the specific mov opcode (MOV R8, ???)
            verify-operand mov --op0 8
            
            # extract the offset
            operand 1

            # define this offset into the struct
            set-struct-member struct_t member_at_r8_offset 'const char *'
	]
}
```

#### Finding several functions in a row

```hjson
{
    name: cool_functions
    instructions: [
            # find string
            find-str 'init_stuff' --null-terminated

            # goto to xref
            xref
    
            # goto function start
            function-start

            # verify only one single result
            unique

            # iterating every 4-byte opcode            
            add-offset-range 0 80 4

            # if mnemonic is bl
            verify-operand bl

            # sort results
            sort

            # store resultset in 'BLs'
            store BLs

            # set first bl to malloc function
            single 0
            goto-ref --code 
            set-name malloc
            set-type 'void *malloc(unsigned int size)'

            # go back to the results from 4 commands ago 
            # (the sort results)
            load BLs

            # rename next symbol :)
            single 1
            goto-ref --code
            set-name free
            set-type 'void free(void *block)'
	]
}
```

#### Conditional branches

```hjson
{
    name: set_opcode_const
    instructions: [
        # goto printf function
        locate printf

        # goto 'case_opcode_bl' if current opcode is bl
        if 'verify-operand bl' case_opcode_bl

            # make: #define is_bl (0)
            clear
            add 0
            set-const is_bl
    
            # finish script by jumping to end
            b end

        # mark as 'case_opcode_bl' label
        label case_opcode_bl

            # make: #define is_bl (1)
            clear
            add 1
            set-const is_bl

        # mark script end
        label end
    ]
}
```

#### Python script to find a list of symbols

```python
from fa.commands.find_str import find_str
from fa.commands.set_name import set_name
from fa import context

def run(interpreter):
    # throw an exception if not running within ida context
    context.verify_ida('script-name')

    # locate the global string
    set_name(find_str('hello world', null_terminated=True),
             'g_hello_world', interpreter)
```

#### Python script to automate SIG files interpreter

```python
TEMPLATE = '''
find-str '{unique_string}'
xref
function-start
unique
set-name '{function_name}'
'''

def run(interpreter):
    for function_name in ['func1', 'func2', 'func3']:
        instructions = TEMPLATE.format(unique_string=function_name, 
                                       function_name=function_name).split('\n')
        
        interpreter.find_from_instructions_list(instructions)
```

#### Python script to dynamically add structs

```python
from fa.commands.set_type import set_type
from fa import fa_types

TEMPLATE = '''
find-str '{unique_string}'
xref
'''

def run(interpreter):
    fa_types.add_const('CONST7', 7)
    fa_types.add_const('CONST8', 8)

    foo_e = fa_types.FaEnum('foo_e')
    foo_e.add_value('val2', 2)
    foo_e.add_value('val1', 1)
    foo_e.update_idb()

    special_struct_t = fa_types.FaStruct('special_struct_t')
    special_struct_t.add_field('member1', 'const char *')
    special_struct_t.add_field('member2', 'const char *', offset=0x20)
    special_struct_t.update_idb()

    for function_name in ['unique_magic1', 'unique_magic2']:
        instructions = TEMPLATE.format(unique_string=function_name, 
                                       function_name=function_name).split('\n')
        
        results = interpreter.find_from_instructions_list(instructions)
        for ea in results:
            # the set_type can receive either a string, FaStruct
            # or FaEnum :-)
            set_type(ea, special_struct_t)
```

### Aliases

Each command can be "alias"ed using the file 
found in `fa/commands/alias` or in `<project_root>/alias`

Syntax for each line is as follows: `alias_command = command`
For example:
```
ppc32-verify = keystone-verify-opcodes --bele KS_ARCH_PPC KS_MODE_PPC32
```

Project aliases have higher priority.

### Loaders

Loaders are the entry point into running FA. 
In the future we'll possibly add Ghidra and other tools.

Please first install the package as follows:

Clone the repository and install locally:

```sh
# clone
git clone git@github.com:doronz88/fa.git
cd fa

# install
python -m pip install -e .
```

#### IDA

Within IDA Python run:

```python
from fa import ida_plugin
ida_plugin.install()
```

You should get a nice prompt inside the output window welcoming you
into using FA. Also, a quick usage guide will also be printed so you 
don't have to memorize everything.

Also, an additional `FA Toolbar` will be added with quick functions that
are also available under the newly created `FA` menu.

![FA Menu](https://github.com/doronz88/fa/raw/master/fa/res/screenshots/menu.png "FA Menu")

A QuickStart Tip:

`Ctrl+6` to select your project, then `Ctrl+7` to find all its defined symbols.


You can also run IDA in script mode just to extract symbols using:

```sh
ida -S"fa/ida_plugin.py <signatures-root> --project-name <project-name> --symbols-file=/tmp/symbols.txt" foo.idb
```


#### ELF

In order to use FA on a RAW ELF file, simply use the following command-line:

```sh
python elf_loader.py <elf-file> <signatures_root> <project>
```

### Available commands

See [commands.md](commands.md)


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "fa",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "doronz88 <doron88@gmail.com>",
    "keywords": "reverse-engineering, ida, automation, signatures, symbols",
    "author": null,
    "author_email": "doronz88 <doron88@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/d9/a1/decd744413cface378d6e3db2a92010cc86565a819e9ed08d1b6df5806f7/fa-1.0.8.tar.gz",
    "platform": null,
    "description": "![Python application](https://github.com/doronz88/fa/workflows/Python%20application/badge.svg)\n\n# FA\n\n## What is it?\n\nFA stands for Firmware Analysis and intended **For Humans**.\n\nFA allows one to easily perform code exploration, symbol searching and \nother functionality with ease.\n\nUsually such tasks would require you to understand complicated APIs,\nwrite machine-dependant code and perform other tedious tasks.\nFA is meant to replace the steps one usually performs like a robot \n(find X string, goto xref, find the next call function, ...) in \na much friendlier and maintainable manner. \n\nThe current codebase is very IDA-plugin-oriented. In the future I'll\nconsider adding compatibility for other disassemblers such as:\nGhidra, Radare and etc...\n\n\nPull Requests are of course more than welcome :smirk:.\n\n## Requirements\n\nSupported IDA 8.0+.\n\nIn your IDA's python directory, run:\n\n```sh\npython -m pip install -r requirements.txt\n```\n\nAnd for testing:\n```sh\npython -m pip install -r requirements_testing.txt\n```\n\n## Where to start?\n\nBefore using, you should understand the terminology for: \nProjects, SIG files and Loaders.\n\nSo, grab a cup of coffee, listen to some [nice music](https://www.youtube.com/watch?v=5rrIx7yrxwQ), and please devote \na few minutes of your time into reading this README.\n\n### Projects\n\nThe \"project\" is kind of the namespace for different signatures.\nFor example, either: linux, linux_x86, linux_arm etc... are good \nproject names that can be specified if you are working on either \nplatforms. \n\nBy dividing the signatures into such projects, Windows symbols for \nexample won't be searched for Linux projects, which will result \nin a better directory organization layout, better performance and\nless rate for false-positives. \n\nThe signatures are located by default in the `signatures` directory.\nIf you wish to use a different location, you may create `config.ini`\nat FA's root with the following contents:\n\n```ini\n[global]\nsignatures_root = /a/b/c\n```\n\n### SIG format\n\nThe SIG format is a core feature of FA regarding symbol searching. Each\nSIG file is residing within the project directory and is automatically searched\nwhen requested to generate the project's symbol list.\n\nThe format is Hjson-based and is used to describe what you, \n**as a human**, would do in order to perform the given task (symbol searching\nor binary exploration).\n\nSIG syntax (single):\n```hjson\n{\n    name: name  \n    instructions : [\n        # Available commands are listed below\n        command1\n        command2\n    ]\n}\n```\n\nEach line in the `instructions` list behaves like a shell\ncommand-line that gets the previous results as the input \nand outputs the next results\nto the next line.\n\nConfused? That's alright :grinning:. [Just look at the examples below](#examples)\n\nUser may also use his own python script files to perform \nthe search. Just create a new `.py` file in your project \ndirectory and implement the `run(interpreter)` method. \nAlso, the project's path is appended to python's `sys.path` \nso you may import your scripts from one another.\n\nTo view the list of available commands, [view the list below](#available-commands)\n\n### Examples\n\n#### Finding a global struct\n\n```hjson\n{\n    name: g_awsome_global,\n    instructions: [\n            # find the byte sequence '11 22 33 44'\n            find-bytes '11 22 33 44'\n\n            # advance offset by 20\n            offset 20\n\n            # verify the current bytes are 'aa bb cc dd'\n            verify-bytes 'aa bb cc dd'\n\n            # go back by 20 bytes offset\n            offset -20\n\n            # set global name\n            set-name g_awsome_global\n\t]\n}\n```\n\n#### Find function by reference to string\n\n```hjson\n{\n    name: free\n    instructions: [\n            # search the string \"free\"\n            find-str 'free' --null-terminated\n\n            # goto xref\n            xref\n\n            # goto function's prolog\n            function-start\n\n            # reduce to the singletone with most xrefs to\n            max-xrefs\n\n            # set name and type\n            set-name free\n            set-type 'void free(void *block)'\n\t]\n}\n```\n\n#### Performing code exploration\n\n```hjson\n{\n    name: arm-explorer\n    instructions: [\n            # search for some potential function prologs\n            arm-find-all 'push {r4, lr}'\n            arm-find-all 'push {r4, r5, lr}'\n            thumb-find-all 'push {r4, lr}'\n            thumb-find-all 'push {r4, r5, lr}'\n\n            # convert into functions\n            make-function\n\t]\n}\n```\n\n#### Performing string exploration\n\n```hjson\n{\n    name: arm-string-explorer\n    instructions: [\n            # goto printf\n            locate printf\n\n            # iterate every xref\n            xref\n\n            # and for each, go word-word backwards\n            add-offset-range 0 -40 -4\n\n            # if ldr to r0\n            verify-operand ldr --op0 r0\n\n            # go to the global string\n            goto-ref --data\n\n            # and make it literal\n            make-literal\n\t]\n}\n```\n\n#### Finding enums and constants \n\n```hjson\n{\n    name: consts-finder\n    instructions: [\n            # goto printf\n            locate printf\n\n            # iterate all its function lines\n            function-lines\n\n            # save this result\n            store printf-lines\n            \n            # look for: li r7, ???\n            verify-operand li --op0 7\n\n            # extract second operand\n            operand 1\n\n            # define the constant\n            set-const IMPORTANT_OFFSET\n\n            # load previous results\n            load printf-lines\n            \n            # look for: li r7, ???\n            verify-operand li --op0 8\n\n            # get second operand\n            operand 1\n\n            # set this enum value\n            set-enum important_enum_t some_enum_key\n\t]\n}\n```\n\n#### Adding struct member offsets \n\n```hjson\n{\n    name: structs-finder\n    instructions: [\n            # add hard-coded '0' into resultset\n            add 0\n\n            # add a first member at offset 0\n            set-struct-member struct_t member_at_0 'unsigned int'\n\n            # advance offset by 4\n            offset 4            \n\n            # add a second member\n            set-struct-member struct_t member_at_4 'const char *'\n\n            # goto function printf\n            locate printf\n\n            # iterate its function lines \n            function-lines\n\n            # look for the specific mov opcode (MOV R8, ???)\n            verify-operand mov --op0 8\n            \n            # extract the offset\n            operand 1\n\n            # define this offset into the struct\n            set-struct-member struct_t member_at_r8_offset 'const char *'\n\t]\n}\n```\n\n#### Finding several functions in a row\n\n```hjson\n{\n    name: cool_functions\n    instructions: [\n            # find string\n            find-str 'init_stuff' --null-terminated\n\n            # goto to xref\n            xref\n    \n            # goto function start\n            function-start\n\n            # verify only one single result\n            unique\n\n            # iterating every 4-byte opcode            \n            add-offset-range 0 80 4\n\n            # if mnemonic is bl\n            verify-operand bl\n\n            # sort results\n            sort\n\n            # store resultset in 'BLs'\n            store BLs\n\n            # set first bl to malloc function\n            single 0\n            goto-ref --code \n            set-name malloc\n            set-type 'void *malloc(unsigned int size)'\n\n            # go back to the results from 4 commands ago \n            # (the sort results)\n            load BLs\n\n            # rename next symbol :)\n            single 1\n            goto-ref --code\n            set-name free\n            set-type 'void free(void *block)'\n\t]\n}\n```\n\n#### Conditional branches\n\n```hjson\n{\n    name: set_opcode_const\n    instructions: [\n        # goto printf function\n        locate printf\n\n        # goto 'case_opcode_bl' if current opcode is bl\n        if 'verify-operand bl' case_opcode_bl\n\n            # make: #define is_bl (0)\n            clear\n            add 0\n            set-const is_bl\n    \n            # finish script by jumping to end\n            b end\n\n        # mark as 'case_opcode_bl' label\n        label case_opcode_bl\n\n            # make: #define is_bl (1)\n            clear\n            add 1\n            set-const is_bl\n\n        # mark script end\n        label end\n    ]\n}\n```\n\n#### Python script to find a list of symbols\n\n```python\nfrom fa.commands.find_str import find_str\nfrom fa.commands.set_name import set_name\nfrom fa import context\n\ndef run(interpreter):\n    # throw an exception if not running within ida context\n    context.verify_ida('script-name')\n\n    # locate the global string\n    set_name(find_str('hello world', null_terminated=True),\n             'g_hello_world', interpreter)\n```\n\n#### Python script to automate SIG files interpreter\n\n```python\nTEMPLATE = '''\nfind-str '{unique_string}'\nxref\nfunction-start\nunique\nset-name '{function_name}'\n'''\n\ndef run(interpreter):\n    for function_name in ['func1', 'func2', 'func3']:\n        instructions = TEMPLATE.format(unique_string=function_name, \n                                       function_name=function_name).split('\\n')\n        \n        interpreter.find_from_instructions_list(instructions)\n```\n\n#### Python script to dynamically add structs\n\n```python\nfrom fa.commands.set_type import set_type\nfrom fa import fa_types\n\nTEMPLATE = '''\nfind-str '{unique_string}'\nxref\n'''\n\ndef run(interpreter):\n    fa_types.add_const('CONST7', 7)\n    fa_types.add_const('CONST8', 8)\n\n    foo_e = fa_types.FaEnum('foo_e')\n    foo_e.add_value('val2', 2)\n    foo_e.add_value('val1', 1)\n    foo_e.update_idb()\n\n    special_struct_t = fa_types.FaStruct('special_struct_t')\n    special_struct_t.add_field('member1', 'const char *')\n    special_struct_t.add_field('member2', 'const char *', offset=0x20)\n    special_struct_t.update_idb()\n\n    for function_name in ['unique_magic1', 'unique_magic2']:\n        instructions = TEMPLATE.format(unique_string=function_name, \n                                       function_name=function_name).split('\\n')\n        \n        results = interpreter.find_from_instructions_list(instructions)\n        for ea in results:\n            # the set_type can receive either a string, FaStruct\n            # or FaEnum :-)\n            set_type(ea, special_struct_t)\n```\n\n### Aliases\n\nEach command can be \"alias\"ed using the file \nfound in `fa/commands/alias` or in `<project_root>/alias`\n\nSyntax for each line is as follows: `alias_command = command`\nFor example:\n```\nppc32-verify = keystone-verify-opcodes --bele KS_ARCH_PPC KS_MODE_PPC32\n```\n\nProject aliases have higher priority.\n\n### Loaders\n\nLoaders are the entry point into running FA. \nIn the future we'll possibly add Ghidra and other tools.\n\nPlease first install the package as follows:\n\nClone the repository and install locally:\n\n```sh\n# clone\ngit clone git@github.com:doronz88/fa.git\ncd fa\n\n# install\npython -m pip install -e .\n```\n\n#### IDA\n\nWithin IDA Python run:\n\n```python\nfrom fa import ida_plugin\nida_plugin.install()\n```\n\nYou should get a nice prompt inside the output window welcoming you\ninto using FA. Also, a quick usage guide will also be printed so you \ndon't have to memorize everything.\n\nAlso, an additional `FA Toolbar` will be added with quick functions that\nare also available under the newly created `FA` menu.\n\n![FA Menu](https://github.com/doronz88/fa/raw/master/fa/res/screenshots/menu.png \"FA Menu\")\n\nA QuickStart Tip:\n\n`Ctrl+6` to select your project, then `Ctrl+7` to find all its defined symbols.\n\n\nYou can also run IDA in script mode just to extract symbols using:\n\n```sh\nida -S\"fa/ida_plugin.py <signatures-root> --project-name <project-name> --symbols-file=/tmp/symbols.txt\" foo.idb\n```\n\n\n#### ELF\n\nIn order to use FA on a RAW ELF file, simply use the following command-line:\n\n```sh\npython elf_loader.py <elf-file> <signatures_root> <project>\n```\n\n### Available commands\n\nSee [commands.md](commands.md)\n\n",
    "bugtrack_url": null,
    "license": "GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007",
    "summary": "Automation tool for locating symbols & structs in binary (primarily IDA focused)",
    "version": "1.0.8",
    "project_urls": {
        "Bug Reports": "https://github.com/doronz88/fa/issues",
        "Homepage": "https://github.com/doronz88/fa"
    },
    "split_keywords": [
        "reverse-engineering",
        " ida",
        " automation",
        " signatures",
        " symbols"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7bb0401ffa3ca8e9c944f4b6234ea5918c7c6fcb45d3e539180d15b2d9dade88",
                "md5": "8795b307d8432233373532133effb65d",
                "sha256": "4a4bf708764ae737176d3e4b47bd449208bd48ca5e382ee7a85f7ef6ba2c6953"
            },
            "downloads": -1,
            "filename": "fa-1.0.8-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "8795b307d8432233373532133effb65d",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 875452,
            "upload_time": "2024-12-08T15:41:44",
            "upload_time_iso_8601": "2024-12-08T15:41:44.622185Z",
            "url": "https://files.pythonhosted.org/packages/7b/b0/401ffa3ca8e9c944f4b6234ea5918c7c6fcb45d3e539180d15b2d9dade88/fa-1.0.8-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d9a1decd744413cface378d6e3db2a92010cc86565a819e9ed08d1b6df5806f7",
                "md5": "d65e2c6499bb7912e79f60dab9469213",
                "sha256": "ce439a82563da2e0f7b3ad84c8a82487a2d5a44671de61c1a3052c093372e246"
            },
            "downloads": -1,
            "filename": "fa-1.0.8.tar.gz",
            "has_sig": false,
            "md5_digest": "d65e2c6499bb7912e79f60dab9469213",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 859671,
            "upload_time": "2024-12-08T15:41:46",
            "upload_time_iso_8601": "2024-12-08T15:41:46.685795Z",
            "url": "https://files.pythonhosted.org/packages/d9/a1/decd744413cface378d6e3db2a92010cc86565a819e9ed08d1b6df5806f7/fa-1.0.8.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-12-08 15:41:46",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "doronz88",
    "github_project": "fa",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "keystone-engine",
            "specs": []
        },
        {
            "name": "capstone",
            "specs": []
        },
        {
            "name": "click",
            "specs": []
        },
        {
            "name": "hjson",
            "specs": []
        },
        {
            "name": "future",
            "specs": []
        },
        {
            "name": "configparser",
            "specs": []
        },
        {
            "name": "six",
            "specs": []
        },
        {
            "name": "rpyc",
            "specs": []
        },
        {
            "name": "click",
            "specs": []
        },
        {
            "name": "ipython",
            "specs": []
        },
        {
            "name": "termcolor",
            "specs": []
        }
    ],
    "lcname": "fa"
}
        
Elapsed time: 0.40274s