import json
import base64
import struct
import binascii

def read_amf_string(data, offset):
    # AMF0 String: 0x02 + U16 Len + Bytes
    # AMF3 String: U29 Len/Ref + Bytes
    # This is a heuristic parser, assuming we are inside the AMF structure
    pass

def decode_u29(data, offset):
    n = 0
    result = 0
    for i in range(4):
        b = data[offset + i]
        if i < 3:
            result = (result << 7) | (b & 0x7F)
            if (b & 0x80) == 0:
                return result, offset + i + 1
        else:
            result = (result << 8) | b
            return result, offset + i + 1
    return result, offset + 4

def parse_amf_packet(data):
    offset = 0
    try:
        ver = struct.unpack(">H", data[offset:offset+2])[0]; offset += 2
        hc = struct.unpack(">H", data[offset:offset+2])[0]; offset += 2
        # Skip headers
        bc = struct.unpack(">H", data[offset:offset+2])[0]; offset += 2
        
        bodies = []
        for i in range(bc):
            # Target URI
            l = struct.unpack(">H", data[offset:offset+2])[0]; offset += 2
            target = data[offset:offset+l].decode('utf-8', 'ignore'); offset += l
            
            # Response URI
            l = struct.unpack(">H", data[offset:offset+2])[0]; offset += 2
            resp_uri = data[offset:offset+l].decode('utf-8', 'ignore'); offset += l
            
            # Length
            l = struct.unpack(">I", data[offset:offset+4])[0]; offset += 4
            
            # Body Content
            # Usually AMF0 Strict Array for loginUser args
            body_start = offset
            marker = data[offset]; offset += 1
            
            args = []
            if marker == 0x0A: # AMF0 Strict Array
                count = struct.unpack(">I", data[offset:offset+4])[0]; offset += 4
                for _ in range(count):
                    # Elements
                    elem_marker = data[offset]; offset += 1
                    if elem_marker == 0x11: # AMF3
                        # AMF3 encoded element
                        amf3_marker = data[offset]; offset += 1
                        if amf3_marker == 0x09: # Array
                            # U29 count
                            ref, offset = decode_u29(data, offset)
                            acount = ref >> 1
                            # Empty key string (0x01)
                            if data[offset] == 0x01: offset += 1
                            
                            # Parse array items
                            arr_items = []
                            for k in range(acount):
                                t = data[offset]; offset += 1
                                if t == 0x06: # String
                                    ref, offset = decode_u29(data, offset)
                                    slen = ref >> 1
                                    sval = data[offset:offset+slen].decode('utf-8', 'ignore')
                                    offset += slen
                                    arr_items.append(sval)
                                elif t == 0x04: # Integer
                                    ref, offset = decode_u29(data, offset)
                                    ival = ref >> 1 # Simplified, might be signed
                                    arr_items.append(ival)
                                elif t == 0x05: # Double
                                    dval = struct.unpack(">d", data[offset:offset+8])[0]
                                    offset += 8
                                    arr_items.append(dval)
                                elif t == 0x00: # Undefined
                                    arr_items.append("Undefined")
                                elif t == 0x01: # Null
                                    arr_items.append(None)
                                else:
                                    arr_items.append(f"UnknownType_{hex(t)}")
                            args.append(arr_items)
                        else:
                            args.append(f"AMF3_Type_{hex(amf3_marker)}")
                    else:
                        args.append(f"AMF0_Type_{hex(elem_marker)}")
            
            bodies.append({"target": target, "args": args})
            
        return bodies
    except Exception as e:
        return f"Error parsing: {e}"

def main():
    path = r'd:\NEW\kingjoki\play.ninjasage.har'
    with open(path, 'r', encoding='utf-8') as f:
        har = json.load(f)
        
    found_success = False
    
    for entry in har['log']['entries']:
        req = entry['request']
        url = req['url']
        
        if 'play.ninjasage.id/amf' in url:
            txt = req.get('postData', {}).get('text', '')
            if 'loginUser' in txt:
                print(f"\n--- Found loginUser Request ---")
                
                # Decode Request
                try:
                    data = base64.b64decode(txt)
                except:
                    data = txt.encode('latin1', 'ignore') # Fallback
                
                # Parse Request
                parsed_req = parse_amf_packet(data)
                print("Request Body:", parsed_req)
                
                # Check Response Status
                resp = entry['response']
                print(f"Response Status Code: {resp['status']}")
                
                # Parse Response Body
                r_txt = resp['content'].get('text', '')
                try:
                    r_data = base64.b64decode(r_txt)
                except:
                    # Often response is raw binary in HAR but JSON string encoded
                    # or it's base64. Let's try base64 first, then latin1
                    try:
                        r_data = r_txt.encode('latin1')
                    except:
                        r_data = b''
                
                # Simple check for success in response bytes
                # Look for "status" ... 1
                # AMF3 object key "status" -> 0D 73 74 61 74 75 73
                # Value 1 -> 04 01 (Integer 1)
                
                if b'\x0dstatus\x04\x01' in r_data:
                    print(">>> SUCCESSFUL LOGIN DETECTED <<<")
                    found_success = True
                    
                    # Extract args from the parsed request
                    if isinstance(parsed_req, list) and len(parsed_req) > 0:
                        args_list = parsed_req[0]['args'][0] # The inner array
                        print("\nExtracted Arguments for Cracking:")
                        for idx, val in enumerate(args_list):
                            print(f"Arg {idx+1}: {val}")
                            
                        # Save specifically the ones we need
                        # Args are usually: [user, pass_hash, timestamp, ?, ?, token, hash, id, ?]
                        # 0: user
                        # 1: pass
                        # 2: timestamp
                        # 5: token
                        # 6: hash
                        
                        if len(args_list) >= 7:
                            print("\nCandidate Data for Hash Cracking:")
                            print(f"Username: {args_list[0]}")
                            print(f"Password Hash: {args_list[1]}")
                            print(f"Timestamp: {args_list[2]}")
                            print(f"Token: {args_list[5]}")
                            print(f"Target Hash: {args_list[6]}")
                            
                            # Save to file for the cracker
                            with open("crack_target.json", "w") as f2:
                                json.dump({
                                    "username": args_list[0],
                                    "password_hash": args_list[1],
                                    "timestamp": args_list[2],
                                    "token": args_list[5],
                                    "target_hash": args_list[6]
                                }, f2, indent=2)
                                print("Saved candidate data to crack_target.json")
                else:
                    print("Response does not indicate success (status: 1 not found).")
                    # Still print args to see what was sent
                    if isinstance(parsed_req, list) and len(parsed_req) > 0:
                         print("Args sent:", parsed_req[0]['args'])

if __name__ == "__main__":
    main()
