← Blog

JOJO’S SHENANIGANS

Inside Mibo, part 2: P2P tunnels, native decoders, and the birth of Noctua

From UDP signaling to audio and video in Noctua: datagrams, PTCP, C bridges, bounded queues, and the failures we had to understand.

The path from a P2P tunnel through native decoders to Noctua.

TL;DR: what was built#

We reached video and audio in Noctua through an independent SDK; turning that result into an application and migrating to the Core player exposed problems a first frame could not reveal.

For 12 cameras, Mibo Cloud for Android advertises packages from R$ 193.90 to R$ 439.90 per month, depending on retention. The service stores events, not continuous video. A later section estimates a hypothetical owner-controlled storage setup; it is not a Noctua feature available today.

Buying a camera should not dictate the only place its footage can go. That choice still has to respect the law and the rights of people recorded. Brazil’s Civil Code, Article 1,228 addresses use of property, while the Consumer Defense Code, Article 6(III) requires clear information. Article 39(I) prohibits unlawful tying. The evidence here does not support that accusation: Intelbras itself documents local alternatives for compatible models.

Part one ended with a frame received from the authorized camera and decoded by an independent client, without running the proprietary application or SDK. Moving that path into an application raised less photogenic questions: where the client may connect, how to reassemble out-of-order bytes, when to send control traffic, who releases each buffer, and how much delay to tolerate before dropping a frame.

The scope is a laboratory build of Noctua with the SDK and Core Engine used in testing. Noctua and SDK source remain private. Camera B video and audible audio were confirmed in the macOS ARM64 app before migration to the current Core player. That confirmation and earlier NativeAOT smokes are historical evidence; they do not automatically validate the new A/V integration. Compatibility across other models, firmware, networks, and operating systems still needs measurement.

Screenshots come from the real interface; their captions say whether the screen contains execution data or just an empty grid. The two interactive labs instead use invented bytes based on verified structures. They do not contact cameras or send packets. A real capture with identifiers erased would look more faithful than it actually is.

Actual Noctua grid with four unassigned positions
Noctua / macOS ARM64 / actual interface, no feeds; not playback evidenceOpen screenshot ↗

Twelve cameras: recording, streaming, and the cost of dependency#

The cameras have been purchased and authorized video already reaches the client. Keeping continuous footage from 12 devices in a searchable, exportable archive is a separate job: it depends on each model’s interfaces and a recorder that does not yet exist here. Event-retention subscriptions add recurring cost to the hardware. They also create dependence on an app, firmware, and service that may change while the sensor and lens still work. That is a risk of lost utility, not a legal finding of planned obsolescence. Intelbras documents local recording options for compatible models.

Intelbras’s official GDI offers an HTTP API and video-stream creation, but requires a business registration with a Brazilian company ID and a video plan with request and bandwidth quotas. It is a real integration route, not an open, unlimited consumer API; we have not tested it or included it in the Mibo Cloud calculation below.

Intelbras itself documents microSD and DVR/NVR over ONVIF or Intelbras-1 for compatible cameras. Those routes deserve model- and topology-specific evaluation. Mibo Cloud is different: its terms say subscriptions are per device and video upload is triggered by motion. Three, seven, or thirty days describe event retention, not a promise to record every second continuously. Mibo+ offers features such as card backup and cloud photos, but it should not be confused with a Mibo Cloud video plan.

What Mibo Cloud would cost for 12 cameras#

Intelbras’s public “Novos Preços Mibo Cloud Android” table lists packages for 1 through 15 cameras. For 12 compatible cameras in one package, its advertised Brazilian-real prices are:

Data table 01
Event retention1 camera/month12 cameras/month12 cameras/year upfront
Basic — 3 daysR$ 16.90R$ 193.90R$ 1,939.90
Standard — 7 daysR$ 20.50R$ 214.90R$ 2,099.90
Premium — 30 daysR$ 40.50R$ 439.90R$ 4,099.90

The annual price is the advertised upfront payment, not monthly × 12. Terms on another Intelbras page still show different single-device prices, and an operating-system storefront may offer something else; this is an advertised Android table, not a checkout quote. Eligibility of the 12 models, possible extra channels for multi-lens units, and the offer for a particular account have not been verified. No plan was purchased for this analysis.

A hypothetical scenario: recordings at an owner-chosen destination#

That footage could go to a local disk or a service contracted by the owner. That is the purpose of the exercise below: to estimate an alternative storage destination without assuming access to Intelbras’s cloud.

Imagine a recorder retaining compressed streams from 12 cameras around the clock and uploading batches to Cloudflare R2 every four hours. The recorder is still hypothetical, but the SDK already exposes H.264 Annex B and AAC ADTS through OpenEncodedMediaAsync and EncodedUnits(), alongside its decoded RGBA and PCM API. What remains is retaining those units in playable files, muxing a container, managing retention, and implementing continuous upload. A compressed API neither supplies those functions nor requires re-encoding frames.

The observed iM3B-C unit delivered Full HD video; in reported app observation, the rate received by the SDK stayed below 700 kb/s most of the time. That metric is a cumulative average of accepted DHAV records without network overhead, and no long-duration measurement has been published for all 12 cameras. Using 500 kb/s per camera only as a hypothesis, the combined batches would total 10.8 GB every four hours, and 30 days of retention would occupy 1,944 GB. At the R2 Standard price, that would be about US$ 29.01/month for storage alone, or R$ 145.05 at an illustrative rate of R$ 5 per dollar. This is a steady-state approximation with a full retention window, no extra retention, and the account’s 10 free GB-month still available. R2 bills the average daily peaks over the billing period and rounds billable units up. Starting with an empty bucket gives the first month a different average occupancy; upload and deletion cadence can also change the amount.

Mibo Cloud Premium advertises R$ 439.90/month for 12 eligible cameras, but its terms describe motion-triggered event retention, not continuous video. The terms also say recordings use the economy stream, whereas the observation informing the R2 hypothesis was in Full HD; we do not assign a specific resolution to the economy stream. The figures therefore do not compare equivalent services in either temporal coverage or video quality. The R2 estimate also excludes the recorder, electricity, connectivity, operations, taxes, and possible extra bytes for audio and container overhead. Average bitrate across all 12 devices and long-duration recording feasibility still need measurement; batching changes disk and bandwidth requirements, not total stored volume.

The calculation sizes an owner-chosen destination for authorized sessions. This build has neither 24/7 recording nor owner-controlled upload, and the SDK currently supports four concurrent streams.

From a first frame to an actual client#

A script exits after showing that it can decode one frame. Noctua has to keep running when a camera does not answer, selection changes during opening, or the window closes while a block is still in the decoder. Every operation needs cancellation; whoever receives a frame must release it. A late reply from one account must not reach the next session.

Responsibilities are split as follows: the Mibo SDK handles authentication, authorization, discovery, and transport, exposing both compressed-unit and decoded-frame APIs. The application handles selection, grid positions, bounded recovery, and the audible source. In the current application path, Core also decodes the compressed units and handles the player, window, controls, pixel presentation, and audio output. The SDK does not depend on a scene or open a sound device. That separation lets us test a network failure without rendering a window and test an audio clock without contacting the vendor.

A frame is a resource. Pixels and PCM occupy meaningful amounts of memory. A slow consumer cannot turn every delayed image into another retained allocation. The API makes disposal explicit, and queues have fixed capacities. A frame removed from a queue becomes the consumer’s responsibility; one discarded by saturation must be disposed by the queue’s owner. Returning a pooled array too early is particularly dangerous because another owner may overwrite it while a renderer is still reading it.

Each real 1/2/4 grid position has an independent opening, reading, and closing lifecycle. Replacement waits for local cleanup before starting another session in that position. One camera’s failure must not stop the others. The SDK’s four-stream budget remains an actual limit: drawing nine rectangles does not provide capacity for nine cameras. Nor do we infer PTZ capability from the presence of a control in a design.

Diagnostics record the layer, correlated activity, attempt, and duration. Error codes remain available; arbitrary network messages do not enter the log. That turns “video unavailable” into a failure at a specific stage without dumping account data to learn what happened.

Who talks to whom: hosts and trust boundaries#

In the examined baseline, app-sp2.easy4ipcloud.com and app-br.easy4ipcloud.com took part in the account HTTPS path. www-v2.easy4ipcloud.com appeared in signaling; www.easy4ipcloud.com appeared as an alternative in authenticated configuration. These roles were observed in this run, not guaranteed as a permanent server list. A hostname alone does not reveal the media route.

What each observed destination does#

  • app-br.easy4ipcloud.com — account entry. We saw HTTPS /pcs/v1 calls for two-step verification state and GetToken. The response returned a token, session, and entry route; it did not authorize every returned URL or identify a video peer.
  • app-sp2.easy4ipcloud.com — authenticated session. Login, GlobalConfigGet, inventory, and camera details used this host in the baseline. Routing can change, so the client accepts only known account hosts.
  • www-v2.easy4ipcloud.com — initial signaling. Authenticated configuration listed it as the p2p-v2 service; this run used UDP port 8800 for registration and discovery. A response can name another destination: the hostname does not show that media traverses this server.
  • www.easy4ipcloud.com — alternate service. The p2p entry can provide a second DNS pool for validating an Info destination. Its presence does not prove signaling fallback occurred.
  • Info, candidates, and peer — discovered destinations. An accepted Info destination had to be a public IPv4 address in the admitted DNS pool on port 8803. Negotiation produced candidates for the media path; camera IPs, ephemeral ports, and topology vary. A remote candidate gets no unrestricted access to the local network.

GlobalConfigGet supplies services by type, host, and port. The SDK selects p2p-v2 for initial signaling and considers p2p only for additional Info validation. It therefore resolves and compares addresses instead of trusting a field returned by the service.

Dahua’s notice about Imou Cloud domains describes www.easy4ipcloud.com for device login and registration, with dynamic IPs. That is context for the service family, not a specification of this Mibo session: our measured role comes from baseline calls and configuration, and can differ from the role of a device registering itself. We do not assign app-br, app-sp2, or www-v2 to a particular operator merely because they share a domain.

Account HTTPS, UDP discovery, Info, and peer separated by validation boundaries
01 / Observed public hosts; peer addresses omittedOpen diagram ↗

Video bootstrap begins with information obtained for the requested device. The reader binds encrypted camera credentials to the expected identifier and extracts service entries. Authenticated configuration is an input to policy, not blanket permission to contact anything written in it. Schema, port, address shape, and association with the operation require validation. UDP discovery introduces further external data and another boundary.

“The service answered” and “the address in its answer is acceptable for this client” are different propositions. A naive consumer could turn a network field into arbitrary probing of the local network. Our client restricts public Info destinations to admitted DNS pools and treats local candidates through a dedicated policy. Exclusions for gateways, DNS servers, and the host itself come from host interfaces; a remote message does not decide which local machines are safe.

Not every session is a “direct tunnel.” A service can help find the peer without carrying media bytes; a channel called proxy can live inside a direct camera connection. Identifying the route of a given run requires correlating sockets, endpoints, and traffic. Class names and binary strings are not enough.

Public hostnames are useful documentation and diagnostic information. Camera addresses, ephemeral ports, session URLs, and identifiers need not accompany that visibility. Any invented address in an illustration is identified as documentary. Rejected destinations also remain outside logs: the rejection reason is observable without publishing a map of someone’s private network. This matters when diagnostics leave the original machine for a bug report.

SK: cryptographic material shipped with the application#

SK is shared cryptographic material associated with the application profile; it is neither the account password nor a terminal’s unique identity.

Our examined source is Mibo Smart Android 3.1.1, build 100315. Noctua carries a versioned compatibility profile for that baseline. We treat material distributed in a release application as pseudo-sensitive: its identification and use deserve care, but security cannot depend on the binary’s owner being unable to observe it. This article identifies the artifact without publishing its literal contents.

In Noctua configuration, the application principal and credential are separate inputs. The observed parser prefixes raw AK with default\ before supplying it to the SaaS signer. The credential called SK participates in that signing path. Static parameters used in camera-credential and Info protection are separate. A configuration asset may contain all of them; proximity in a file does not give them the same cryptographic role.

The SHA-256 identifier computed over the stored credential’s UTF-8 bytes in this baseline is:

0c2a64505a55cf78b346da99fa508b6c79a231a26002447b593c40baed86f842

The interface displays only the first six bytes, 0C2A64505A55, for visual comparison. This prefix is not a vendor signature, provenance certificate, or authorization proof. It helps notice when builds use different material. Even a full digest identifies content; provenance still depends on extraction records and the examined release. Calling this fingerprint a signature would give it properties the implementation does not provide.

Application material, terminal identity, account credential, and session have different roles
02 / Four identities that must not collapse into one tokenOpen diagram ↗

The signer operates on uncompressed JSON bytes. It hashes the body and constructs a canonical representation containing method, path, content type, and headers in fixed order. An optional session header’s position and the final newline are signed bytes. Application credentials use one MD5 hexadecimal pass before HMAC-SHA256; the password variant uses another pass. We reproduce a compatibility format, not recommend a new password-storage construction.

The shape below is synthetic; \n means the LF byte, and digest/principal are placeholders. It is not a signable request.

POST\n
/pcs/v1/example\n
<body-md5-base64>\n
application/json\n
x-pcs-apiver:<revision>\n
x-pcs-client-ua:<serialized-UA>\n
x-pcs-date:<date>\n
x-pcs-nonce:<nonce>\n
x-pcs-username:<principal>\n

Serialization changes can break a signature without changing the apparent meaning of the JSON. Whitespace, property order, and a header revision are details that golden vectors must fix. Public tests use synthetic inputs, so canonicalization and derivation can be checked without turning release material into a fixture. The useful property is byte equivalence, not a plausible-looking digest in a log.

What possessing SK enables—and what still depends on the server#

Possessing application material reproduces the stage that uses it; access to accounts and cameras still requires other service-checked conditions.

Noctua creates fresh terminal identifiers locally and persists that identity so every launch does not resemble a new installation. Remaining profile fields match the accepted shape of the examined Android baseline. They do not attest that a desktop is an Android phone or establish server trust on their own. Generating ttid and terminalId is a different concern from knowing SK.

The account still passes authentication and, when required, a two-factor challenge. Requesting and submitting a code are separate actions. An accepted code must drive the proper transition through login and session establishment; closing a challenge screen is not sufficient. The application persists a session only after success and restores it through SDK operations without assuming a local ticket remains acceptable.

Camera authorization does not derive from a label in a list. Opening a selection obtains details and binds the operation to the account and session generation. Replacement or rejection invalidates selections and media. A fingerprint, cached inventory, or knowledge of the protocol does not substitute for server authorization. The fact that a client can identify a camera is not evidence that it may consume that camera.

This map explains why encrypting an embedded asset does not protect its contents from the person executing the client. If the application must use a value, some point exposes it or an operationally equivalent representation. Asset protection may serve other distribution properties; it cannot turn a shared client secret into individual identity proof. Those limits belong in protocol design and server decisions rather than in hopes about unreadable binaries.

Building the P2P path#

We start from authorized camera details and service configuration. Signaling uses a UDP socket connected to the selected destination. Connected here does not mean a TCP-like handshake: it helps fix the expected peer and send/receive behavior for the socket. It does not make delivery reliable or give the conversation cryptographic authentication. Keeping the distinctions explicit prevents a transport convenience from being mistaken for a security guarantee.

The observed sequence includes initial registration, availability discovery, an Info query, and registration using the returned salt. Operation names organize the flow, but tests check datagram structures and their relationships. The expected sequence must occur exactly once, the declared length must match the body, and unrelated replies cannot advance the state. Each accepted transition needs evidence from the current operation.

Registration, discovery, Info, salted registration, probe, and PTCP channel
03 / Opening: every arrow crosses validationOpen diagram ↗

Time and work are bounded. A signaling exchange has a six-second, 64-datagram budget. Malformed responses cannot keep opening alive indefinitely. Cancellation reaches the socket and cleanup; incomplete states cannot remain available to a later attempt as though they were a ready session. This is also important for an application that lets a user switch rapidly between cameras.

A successful login does not explain a media failure. In diagnostics, camera details and configuration arrived normally while the probe failed. Separating those stages kept password and two-factor authentication out of an investigation that had moved to the network. The opening activity links these events without logging a token or private session identifier.

Registration and Info: a valid reply still needs an acceptable destination#

The Info address must be public IPv4, use observed port 8803, and belong to the primary service’s admitted DNS answer set. If authenticated alternate-service configuration exists, its public DNS answers can also be considered. Wrong port, nonpublic address, and a DNS pool miss are different outcomes. We made that distinction visible because one “invalid destination” error hid materially different decisions.

In an execution, an advertised public shard fell outside both admitted pools. We did not solve this by allowing every public IPv4 address. The SDK discarded that endpoint and made an Info query to the registration entry already used, on the expected port, validating that entry against the same DNS policy. This alternative returned usable Info for the authorized camera. Failure there ends opening; the rejected shard is not retried through a hidden fallback.

This observation supports an alternative service route, not a claim that the official app uses precisely our selection algorithm. Establishing that requires its own correlation in the official process. A working compatible solution can differ from unobserved internal choices. Recording the distinction protects later research from inheriting an assumption as though it were a discovered fact.

There remains a residual limitation: UDP signaling does not gain strong authentication merely through sequence and socket-origin checks. Restrictions reduce unwanted paths but do not make the message cryptographic proof of an endpoint. Candidate identity and transaction validation remain necessary in the next exchange. Our policy describes an implemented boundary, not a claim of protocol-wide cryptographic security.

Local candidates, public candidates, and the identity probe#

The implemented variant parses a canonical text list of at most eight RFC1918 addresses and selects no more than two distinct peers. The directly connected subnet must be /24 through /30, with an unprivileged port. Host addresses, gateways, DNS servers, network, and broadcast addresses are excluded. Unknown encoded forms or encryption variants are not guessed. This limits probing scope and makes policy testable with invented network metadata.

Same-subnet membership does not authenticate the camera. The probe complements every byte of a STUN-like structure. Before that transformation it contains type, length, cookie 0x2112A442, a 12-byte transaction, and an identity attribute. Familiar elements from STUN help investigation but do not establish compliance with an entire ICE, STUN, or TURN stack. Similarity needs to remain a reading aid, not an unsupported implementation label.

A response must carry the discovered identity and a transaction accepted in the current exchange. A peer-initiated transaction can also be answered within that exchange. Accepting only a magic value, length, or known port would let a packet from another opening resemble progress. The client must preserve relationships across messages, rather than validate every packet in isolation and discard all context.

A local attempt can leave the socket terminal. Reusing it for a public candidate is not a valid fallback. Registration and discovery belong to the socket; creating another socket and copying only a “registered” flag would also be wrong. The implementation preserves the underlying error and reconstructs the path requiring a fresh socket. The macOS case later in this article made this both a behavioral and diagnostic requirement.

Anatomy of the datagrams#

Every byte needs an offset, a unit, and an acceptance rule before the layout becomes useful to an implementer.1

The first explorer contains four synthetic examples, complete at the level each claims to illustrate. Signaling has an invented text body. The probe uses an invented transaction and identity. The PTCP example is a header without media. The proxy wrapper contains four PLAY bytes, not a full valid request. These are not presented as packets ready to open a camera, and the interface never attempts to transmit them.

LAB 01 / DATAGRAMS

Choose a packet and select a field to inspect its bytes.

UDP / HTTP-shaped

485454502f312e3120323030204f4b0d0a435365713a20370d0a436f6e74656e742d4c656e6774683a20340d0a0d0a626f6479

Fields; zero-based byte offsets
OffsetBytesFieldInterpretation
017StatusStatus line; this is not an HTTP/TCP connection.
179CSeqExpected sequence: 7. Exactly one header.
2621Content-LengthFour body bytes, including the CRLF separator here.
474BodySynthetic body: body. No camera response.

Candidate / probe

0001000c2112a4420102030405060708090a0b0c802a00081122334455667788

Fields; zero-based byte offsets
OffsetBytesFieldInterpretation
02Type0x0001, request; big-endian.
22Length12 attribute bytes.
44Cookie0x2112A442; resemblance to STUN does not establish equivalence.
812TransactionIdentifier invented for this example.
204AttributeType 0x802A, length 8.
248IdentitySynthetic identity. Complement every byte for the wire.

PTCP / header

505443500000000c00000008000000000000006400000050

Fields; zero-based byte offsets
OffsetBytesFieldInterpretation
04MagicASCII PTCP.
44Send offsetSend offset 12; big-endian.
84Receive offsetNext contiguous byte expected: 8.
121Extension control0: no extensions in this example.
131FlagsControl flags; zero in this example.
142WindowWindow field; illustrative zero.
164ClockSynthetic clock 100.
204EchoSynthetic echo 80.

Proxy / wrapper

100000040102030400000000504c4159

Fields; zero-based byte offsets
OffsetBytesFieldInterpretation
01Type0x10 identifies this wrapper.
11ReservedZero in this example.
22LengthInner length 4; big-endian.
44ChannelInvented channel 0x01020304.
84ReservedBytes not interpreted in this example.
124PayloadAbbreviated PLAY text, not a complete request.

Vector field sheets: UDP · Probe · PTCP · Proxy.

In signaling, HTTP-like text is a useful visual cue, but the datagram remains the external unit. The parser requires exactly one CSeq, bound to the request, and exactly one Content-Length describing the remaining body. Our example has four body bytes. A declared length beyond the received datagram does not mean another TCP read will complete the message; that reply must be discarded.

For the probe, switching between logical and wire bytes reveals a simple transformation that makes visual inspection confusing. The cookie no longer appears with its familiar bytes on the wire. Complement is reversible and provides no confidentiality. Finding the correct observation point was the challenge, not defeating encryption. Labeling a dump as before or after transformation prevents comparisons of two different boundaries as though they were identical packets.

PTCP starts with ASCII PTCP. Four-byte send and receive offsets use big-endian. Extension control, flags, window, clock, and echo fields follow. Extension control is zero in this example, but part 1 documents packets with extensions: their body starts after the extension region, not necessarily at byte 24. Its fixed header is 24 bytes, which does not mean the entire datagram is 24 bytes. Capturing only one iovec may preserve a header while missing additional payload vectors. This observer error already appeared during the original research and is worth retaining as part of the implementation story.

The inner wrapper adds a layer: type 0x10, a big-endian length at offset 2, and channel identity at offset 4. Its header occupies 12 bytes. Advancing the external offset using only application bytes misaligns the next message because PTCP counts wrapped bytes. Outbound requests are fragmented into wrappers with at most 1,280 inner bytes. That is an implementation limit, not a universal discovery of network MTU.

Each packet also has a separate SVG field sheet. The explorer is an alternative lens, not a prerequisite for understanding the content. Selecting a field highlights its interval and displays its interpretation; with JavaScript disabled, all four tables remain readable. Readers can inspect layouts offline without enabling any connection to the services discussed in the article. The HTML itself contains the examples rather than downloading them on demand.

PTCP: reassembly, ACKs, and retransmissions over UDP#

Only contiguous bytes reach the media parser; receiving a datagram does not necessarily advance the application.1

The laboratory below uses an abstract byte stream and omits proxy wrappers. Its offsets and ACKs belong to the model; they are not literal PTCP packet values. In part 1, three application bytes advance the transport by 15 bytes because they include the 12-byte wrapper; here, four abstract bytes advance by four.

Consider three four-byte blocks: ABCD, EFGH, and IJKL, at offsets 0, 4, and 8. If offset 4 arrives first, data is pending but the next contiguous byte expected remains zero. When offset 0 arrives, both blocks can be released and the expected offset jumps to 8. Physical arrival was reordered; the consumer received a continuous stream. The diagram’s offset is a byte position rather than a packet number.

LAB 02 / PTCP

Abstract model without proxy wrappers: offsets and ACKs are not literal PTCP values. Reassembly delivers bytes in order: it compares pending conflicts and discards consumed duplicates without comparing their contents.

SENDREASSEMBLYPARSERWaiting for reader action Model ACK 0Pending 0Parser 0
parser / stdout

Expected sequence

  1. In order
    Arrival
    0/ABCD → 4/EFGH → 8/IJKL
    Model ACK
    4 → 8 → 12
  2. Reordering
    Arrival
    4/EFGH → 0/ABCD → 8/IJKL
    Model ACK
    0 → 8 → 12
  3. Loss and retransmission
    Arrival
    0/ABCD → drop(4) → 8/IJKL → 4/EFGH
    Model ACK
    4 → 4 → 4 → 12
  4. Pending conflict
    Arrival
    4/EFGH → 4/XXXX → 0/ABCD
    Model ACK
    0 → terminal rejection

The second laboratory lets readers step through these cases without capture noise. Scenarios are deterministic and small. In the loss scenario, the middle block does not arrive initially, the last remains pending, and retransmission closes the gap. The displayed ACK is the next contiguous byte expected, not a count of received packets. Animation starts only through reader action and can be paused at any step.

Duplicates require semantics too. Different bytes at the same still-pending offset are a conflict: the SDK rejects the session instead of choosing last-write-wins. A fragment entirely behind the delivery cursor is discarded and acknowledged without comparing its contents; it cannot rewrite consumed bytes. A fragment crossing that cursor is rejected. The simulation follows this distinction: EFGH versus XXXX conflict while pending, but a consumed duplicate is not revalidated. The SDK also rejects excessive gaps and ACKs beyond sent data. The laboratory remains a small model, not the entire PTCP state machine.

Real code has storage and timing limits simplified by the illustration. Each pending fragment allows at most four total sends: the initial send and up to three retransmissions, with a minimum interval of 400 ms between sends. Scheduling and network delays can increase that interval. This is not a general TCP-equivalent congestion-control algorithm. Our goal is compatibility with the observed route while retaining bounded resources and explicit rejection of unsupported variants. That scope is narrow enough to test, and broad compatibility would require additional evidence.

A transport ACK is also different from an application-control response. It can confirm reception of request bytes without confirming playback acceptance. The client must still wait for and validate the corresponding control reply. Likewise, contiguous bytes may contain a response fragment, a partial media record, or multiple items together. Reassembly does not eliminate framing work at the next layer.

Tests deliberately fragment and coalesce input to exercise that boundary. A parser that only works when a read coincides with a whole message depends on a transport accident. Correct state waits for incomplete input while refusing lengths that would grow memory without bound. This pairing—state plus an explicit budget—is what turns a protocol sketch into code that can remain alive in a desktop application.

Why a first frame does not guarantee a stable stream#

After candidate confirmation, the PTCP exchange prepares a proxy channel chosen for the session and expects its matching CONN. Only then do playback-request bytes enter that channel. The SDK sends PLAY using the observed method and continues handling controls while receiving media. The stream does not become a plain video file after its first successful response.

One instructive failure looked like periodic disconnection. In Noctua’s HD path, the peer closed playback after about 65.6 seconds without an application keepalive. An earlier Eco observation under different conditions lasted 90 seconds, so we did not turn either duration into a universal timeout. The useful conclusion was that ACKs and media traffic did not replace the application control the peer expected.

The implementation now sends method 2 every 20 seconds after PLAY succeeds, allowing just one outstanding control. If its response is still absent at the next interval, the session fails. Close keeps its reserved sequence. This differs from silently opening repeatedly and calling the result continuous playback. Continuity must be evaluated against the same session and resources rather than a new opening hidden behind the UI.

The keepalive response forced a parser refinement. The observed variant is a 200 with the matching CSeq, no length header, and no body. An ordinary PLAY response does not receive that exception. Only the currently pending keepalive CSeq may use the empty form. This specific rule fixed http-private-length-missing without accepting arbitrary truncated HTTP as a valid control.

Duplicate headers, wrong CSeq, unexpected status, and ambiguous length schemes remain rejected. The exception is confined to the private inner stream; it does not change account HTTP parsing or UDP signaling. An observed peculiarity in one layer must not relax checks everywhere else. Making that boundary visible in tests helped prevent a compatibility fix from becoming general parser permissiveness.

The application may offer bounded recovery for transient failures, using fresh resources and cancellation. It does not automatically replay rejected authentication or turn a destination-policy rejection into a retryable timeout. Attempt counters and opening events expose what happened. A user should be able to distinguish a camera actually delivering frames from an interface merely trying another opening.

From the tunnel to audio and video records#

The inner stream interleaves controls and media records. A six-byte wrapper starting with $ declares a DHAV record’s size. In the studied variant the DHAV header has 24 bytes; the trailer contains dhav and repeats the length in little-endian. Header extensions and an additive framing byte provide additional structure. That byte can detect inconsistency but is not a cryptographic MAC.

For protected records, extension B5 describes a clear prefix, a protected span, and verification/OFB data. Three-byte lengths are little-endian. Before accessing those ranges, the parser checks that the extension fits the header and the protected span fits the record. Decrypting first and validating bounds later would expose lower-level routines to input that should already have been rejected.

Offsets relative to the B5 tag in its 44-byte extension:

Data table 02
OffsetBytesField
01B5 tag
11Size = 44
21Variant = 1
33Clear prefix length, LE
63Protected span length, LE
916Verifier seed
252CRC16 of derived verifier
2716OFB IV
431Uninterpreted suffix

The observed verifier ultimately has only 16 bits, and OFB does not authenticate content. We therefore do not describe the protection as a strong integrity guarantee. Media remains hostile input even after the record passes its check. Neither parser security nor decoder robustness is replaced by the fact that AES appears somewhere in the path. The cryptographic operation and the acceptance policy are separate concerns.

In the audio baseline, the admitted configuration yields one CRC-free ADTS AAC-LC access unit at 16 kHz, mono, with one raw data block. Its declared ADTS length must match the complete unit. Another frequency, multiple blocks, or truncation fails before native decoding. This allowlist describes the observed firmware, not every codec configuration across the Mibo device family.

Video and audio advance separate fine 16-bit millisecond counters with wraparound. The clock layer anchors initial audio against initial video using the shared counter, checking the packed calendar for consistency rather than trusting it as a wall clock. Audio may arrive first; the SDK retains at most 128 such records and one MiB during startup. Exceeding the budget fails explicitly instead of accumulating pre-video audio indefinitely.

After origin selection, timestamps are relative to the source, and audio can begin at a negative time. The SDK yields decoder output without globally sorting it into presentation order. Playback policy belongs to its consumer. That separation prevents the library from assuming a sound device or UI cadence, but requires the app to distinguish packet arrival from the moment samples should be heard.

FFmpeg worked; distribution introduced other decisions#

The initial integration helped verify the media path with an independent implementation. It reduced uncertainty: an invalid image could originate in framing, unprotection, or format interpretation, and comparing output separated those causes. Moving to other decoders does not erase that usefulness. Offline comparisons remain evidence for the tested corpus, not proof that two decoders behave identically for arbitrary inputs.

FFmpeg documents an LGPL base and optional GPL components. Disabling GPL and nonfree is a build decision, not an exemption from redistribution obligations. We considered relinking for our static package: corresponding source, configuration, and required materials must match the distributed artifact. A link to a moving branch does not identify that set by itself. LGPL 2.1 is the normative reference, not this account of our engineering discussion.

The engineering challenge combined .NET NativeAOT, a small bridge, five intended ABIs, and a distribution that remains private. SDK, application, and decoder sources have different release cycles. An intention to publish our own source in the future does not change the requirements of a present distribution. We preferred resolving the actual package composition over making compliance depend on a future promise.

Our observed use was narrow: H.264 images and AAC-LC audio. We evaluated specialized libraries and chose libavc and libxaac while keeping the SDK’s decoded API in RGBA8 and PCM16. Its compressed API lets another consumer own playback. This reduced integration scope but brought its own work: format checks, color conversion, allocator integration, platform patches, and review of the C boundary. A smaller decoder library remains complex native code rather than becoming safe by size alone.

Source provenance became explicit: a fixed commit, verified input archive, and a known build script. The same commit may produce different tar bytes under another Git version or environment; the script accepts recorded digests for the host rather than any file with the expected name. This does not prove codec security, but prevents replacing a dependency unnoticed during a release build.

Local adaptations include libxaac on macOS ARM64 and libavc thread integration on Windows. Building another RID is more than changing a suffix. A script supporting five ABIs is not acceptance evidence for five platforms. Build matrices and native tests must record what actually ran. That distinction belongs both in package documentation and in this article’s claims.

Two OSS libraries, one small C boundary#

We use libavc for H.264 and libxaac for AAC. Their pinned projects use Apache-2.0; LICENSE, applicable NOTICE information, and local-change records remain part of distribution preparation. The Apache-2.0 patent grant has a defined scope. It is not a blanket assurance covering every standard-essential media patent.

In NativeAOT publication, codec static archives and C adapters are linked into the executable. Managed development uses a different native-library loading path. We encountered a missing-library failure there as well. Correcting development copying or resolution neither proves nor invalidates a published executable’s static linkage. Artifact inspection and execution must target the build being claimed.

The interface intentionally exports neither the codecs’ broad configuration surface nor native pointers. In the decoded path, the SDK frames units and returns RGBA8 and PCM16 frames; concrete decoder types stay behind adapters. In the compressed path, OpenEncodedMediaAsync and EncodedUnits() return explicitly disposable H.264 Annex B and AAC ADTS units without invoking the SDK’s decoders. The current Core player consumes this second path. This makes replacement easier and reduces combinations needing tests, without removing the possibility of vulnerabilities in the libraries themselves.

Validated unit, send, output-size probe, copy, and disposal across .NET and C
04 / SDK decoded path; the compressed API delegates decoding to its consumerOpen diagram ↗

Who allocates, who copies, and who frees#

Inputs are borrowed for the call; outputs have an owner until transfer and disposal.

send receives a pointer valid only during the synchronous call, its length, and a timestamp. The C adapter validates arguments independently of the .NET wrapper. H.264 units are bounded at one MiB, dimensions at 1920×1080, and admitted formats at observed variants. The video path rejects B frames because presentation reordering would require another timing contract. Matrix and range come from VUI when present; absence uses the documented limited-range BT.601 behavior.

Output uses two calls. First, receive reports pending output and metadata without copying pixels. Managed code validates dimensions and size before renting an array. The next call supplies destination capacity and copies RGBA8 or PCM16. A short destination reports insufficient capacity without consuming the frame. A length does not become trustworthy merely because a native codec produced it.

Video adapter signatures for the SDK’s decoded path; AAC uses separate symbols and reports samples instead of image dimensions. The compressed API does not invoke these decoders:

int mibo_decoder_send(MiboAvcDecoder *decoder,
    const uint8_t *data, int size, int64_t pts);
int mibo_decoder_receive(MiboAvcDecoder *decoder,
    uint8_t *output, int capacity,
    int *width, int *height, int64_t *pts);
void mibo_decoder_destroy(MiboAvcDecoder **handle);

ABI results are small: -1 rejects, 0 means no output, -2 supports metadata/capacity negotiation, and 1 completes the copy. Video and audio have distinct symbols. After transfer, the managed frame owns its array and returns it cleared to the pool on disposal. Temporary encoded input is cleared after use. No pointer must survive as an undocumented field inside a UI component.

Libavc uses allocation callbacks with a registry outside codec-visible memory: at most 4,096 allocations and 96 MiB per decoder. Libxaac has a separate 16-block, 32-MiB budget. In the video adapter, an unknown or repeated free poisons the decoder; freed blocks are wiped and quarantined until destruction. This reduces immediate reuse inside that lifetime but does not contain arbitrary writes inside the process.

The managed wrapper serializes send, receive, and destruction. destroy takes the handle’s address and clears it before cleanup. Repeated disposal through the same storage does not free it twice. A copied or forged native pointer is not a supported ABI input. That distinction prevents advertising “double-free impossible” as though any hostile C caller were safely isolated from the rest of the process.

The bugs review found in our own bridge#

The first path started with an uninitialized decoder. The routine obtained dimensions, allocated YUV, and could then fail VUI, matrix, or mode validation. Another send on that handle could reuse old planes and dimensions. A later allocation could also replace the old pointer without releasing it. Typical managed sessions disposed the decoder on error, but direct ABI calls and future retry behavior could not rely on that coincidence.

We made failure terminal and organized cleanup to prohibit partial reuse. Review did not stop there. Even after successful initialization, a dimension-changing unit reaches the codec before the adapter receives the new dimensions. Checking width and height after the call does not by itself protect planes allocated for the first image. The order of side effects matters more than the apparent presence of a validation branch.

The bridge now reserves complete YUV420 output planes for its supported maximum: 3,110,400 bytes through 1920×1080. A later image within that limit has capacity before the subsequent check detects change; the change still rejects the stream and requires a new handle. An offline synthetic 64×64-to-1080p test covers this specific transition. It does not prove safety for arbitrary dimensions or every possible codec-internal corruption.

Allocation tests and sanitizer harnesses provide additional evidence where supported. Instrumenting only the adapter does not mean the entire dependency was ASan-built. Residual risk remains inside libavc and libxaac in the app’s process. Containing arbitrary decoder corruption would require a different isolation architecture. We do not append that promise to documentation for an in-process bridge that does not provide it.

The C ABI benchmark separates contract costs such as metadata probing, copying, validation, and decoding. Buffers per second depend on resolution, content, hardware, and the measured path. We do not reuse an isolated number to promise nine simultaneous cameras. Useful performance evidence must include its workload and scope, otherwise precision in the reported number hides uncertainty in what it measures.

A/V in Noctua: queues, clocks, and audio selection#

The current player receives compressed units; its Core session advances with scene elapsed time, not the audio output’s estimated clock.

The SDK retains two contracts. OpenMediaAsync and MediaFrames() deliver decoded RGBA8 and PCM16. OpenEncodedMediaAsync and EncodedUnits() deliver H.264 Annex B and AAC ADTS with timestamps relative to the same source origin. Current Noctua uses the second path: it copies units into StreamingSession and disposes the SDK object. Only one selected camera can emit sound, and muted remains the default.

VideoPlayer calls Advance(elapsed) during scene updates. The session makes a bounded correction toward the chosen delay behind the newest video; a worker decodes due units and the player takes the pending image. Pausing freezes that clock while units keep arriving. Noctua configures a one-minute live window, also capped at 32 MiB of compressed data per session; the byte limit can shorten retention. This cap does not include all codec, pixel, and texture memory.

Compressed SDK units enter a Core session; scene elapsed time advances presentation and decoding feeds video and PCM
05 / Current player: scene clock, compressed cache, and separate outputsOpen diagram ↗

The observed audio output remains mono 16-kHz PCM16 little-endian, sent to Core’s live input and mixed through the existing device. Its ring holds 500 ms at 48 kHz with a 100-ms initial presentation lead. Integer-rate conversion uses zero-order hold. These output characteristics neither make audio the player’s master clock nor establish acoustic synchronization: physical device latency remains outside direct observation.

The previous application path used the selected audio’s estimated clock to release video from an eight-frame queue, about 63.3 MiB in RGBA 1080p. It held up to eight 8,000-byte PCM blocks before the first image, discarded video more than 100 ms late, and reset presentation on discontinuities above 500 ms. Saturation review resolved a real starvation problem in that queue. Those numbers describe that implementation and its tests; they are not the current player’s limits or scheduling policy. The SDK’s 128 startup records described earlier belong to a separate boundary.

In the current path, audible selection controls which PCM reaches output. Replacing or closing a camera, signing out, and suspending the app cancel reading and dispose the corresponding resources. Mute must not be described as recalling samples already handed to hardware. The previous path’s output flushing and recovery policy does not automatically prove how the new integration behaves either.

Human confirmation of audible output and camera B video predates this migration. Current player tests cover caching, composition, ownership, and playback transitions; builds and synthetic captures check other properties. None transfers that human confirmation to the new implementation. Acceptance with authorized devices, perceptual synchronization measurement, and prolonged sessions must be repeated, including with the distributed NativeAOT artifact.

Error 10058 crossed the protocol boundary and reached macOS#

The user launched Noctua from iTerm through dotnet run. The local camera failed during candidate-probe sending while HTTPS and remote registration succeeded. Diagnostics preserved normalized socket code 10058. The same path could work when started another way. That suggested a contextual difference without yet establishing which difference mattered. Repeating a network retry would only reproduce the symptom.

We first fixed error masking. A terminal socket could trigger a later state exception hiding the original send failure. Then temporary instrumentation recorded native-call metadata: result, errno, and private/public destination classification. It did not need payload or address logging to show that UDP send returned EPIPE, errno 32, in that execution. Narrow observations were sufficient to move the investigation forward.

The next evidence came from Network.framework in the same process. A temporary diagnostic, without transmitting additional media, reported localNetworkDenied. macOS was denying local access in the user’s launch context. This is more concrete than inferring permission from a .NET code or from the absence of a popup. Apple’s Local Network Privacy documentation is the platform reference.

The user enabled the corresponding entry under Privacy & Security → Local Network, restarted the context, and confirmed camera video and audio. No firewall disablement, arbitrary destination acceptance, or retry escalation was needed. Hooks and the temporary driver were removed from delivered code. The important result was distinguishing permission denial from camera unavailability, not adding permanent instrumentation to the product.

This investigation also constrains the written conclusion: we can attribute that reproduced case to local-network denial. We cannot retroactively conclude that every historical 10058 had the same cause. An error code is a clue; the conclusion becomes stronger when system observation, process context, and a controlled change agree. The article preserves that distinction rather than turning a useful fix into a universal diagnosis.

The laboratory today, and what remains to validate#

The real 1/2/4 grid opens and closes positions independently. Audio belongs to the selection and uses Core output. Successful authentication persists a session in secure storage; service rejection invalidates the corresponding ticket. Diagnostics expose stages and layers. We do not call this a complete compatibility matrix or a finished product for every Mibo device.

Editorial screenshots distinguish interface from media evidence. An empty grid does not establish playback; the Servers tab groups invoked hosts, while figure 01 explains policy; neither authorizes arbitrary hosts. Noctua shares Core’s visual language, but account and transport decisions remain in the Noctua/Mibo domain. Displaying a green state does not grant the interface additional authority. Captions must explain what the visible state actually means.

Event explorer with filters, layers, operations, and pagination
Noctua / actual inventory events; no account or media dataOpen screenshot ↗
Events grouped by public host invoked by the client
Noctua / Servers tab; policy map appears in figure 01Open screenshot ↗

We still need native acceptance per platform and ABI, prolonged sessions, capacity measurements with more authorized streams, and perceptual synchronization checks. PTZ needs a verified contract and compatible camera; nine streams need device availability and a capacity budget, not merely a layout. New NativeAOT audio integration must be recorded separately from earlier video-only smoke tests.

Distribution work also remains: actual binary composition, notices, dependency source and patches, signing, and installation. We intend to publish our source in the future. That does not make today’s private repository public or promise a schedule. The immediate value of the laboratory is to record decisions and results precisely enough that subsequent work can challenge and improve them.

Appendix A: the cost of obscurity is falling#

Much of reverse engineering means connecting scattered clues. A string appears in Java, an object crosses JNI, a structure changes shape in native code, and the bytes reappear transformed at the socket. Before interpreting an algorithm, we must establish which pieces belong to the same path. Models with larger contexts help keep more of that map available and propose relationships we can investigate.

These tools speed up the work, but every connection still needs checking at the bench. A model can compare canonical requests, identify differences between captures, suggest endianness, and produce a synthetic check. It can also insist on an inactive function or put too much confidence in a name. Initial research encountered precisely these limits: instrumenting the wrong symbol yields missing evidence, while an incomplete hook can produce a false packet.

This is neither a model benchmark nor a universal curve of reverse-engineering cost. In this investigation, models helped connect different artifacts faster. That weakens the assumption that a shared value stays protected mainly because the client is hard to understand.

The issue exists with closed code too. A binary still has to execute its mechanism to work. Obfuscation can raise cost, and integrity checks may complicate instrumentation; neither is equivalent to server authorization. If recovering a constant grants excessive power, the scope of that power is the weakness. If it only identifies an application while credentials, session, and permissions remain independently checked, those boundaries still matter.

SK shows why word choice matters. Calling it simply a secret can imply installation-specific exclusivity; calling it public may understate its operational role. “Shared cryptographic material distributed with the client” better describes what was observed: an identified baseline and a specific use. Engineering should assume that material can be observed and bound what possession means at the server.

The extra context has a trap: a fluent account can combine facts from incompatible revisions. Commits, hashes, and evidence levels helped keep an old hypothesis separate from a new execution. Preserving contradictions and failed attempts made the analysis easier to audit.

Larger context connects evidence; it does not turn a hypothesis into proof#

For packets, a header hypothesis became a vector with offsets and lengths. For media, an unprotection hypothesis was compared with independent output. For the bridge, apparently safe code faced a second send after failure and a resolution transition. On macOS, a socket error became a question to a network framework in the same process. Each conclusion required an observer appropriate to the boundary being claimed.

Models were useful for listing invariants, following error paths, finding ownership inconsistencies, and generating boundary tests. Other answers had to come from outside the model: did the server accept login, did an image reach the viewport, did sound stop, did playback remain in the same session? Execution answers those questions, and some require human observation or measurement equipment. Fluent explanation cannot substitute for those observations.

YUV review demonstrates concrete doubt. The normal managed path discarded the decoder on failure, but someone needed to ask about direct ABI reuse. After that fix, someone needed to ask about a larger first subsequent unit before dimension checking returned. Progress came from changing the question and testing that path, not repeatedly issuing a generic statement that the code looked secure.

There is a practical lesson for system design: boundaries must still work once the client is understood. Server authorization, response-operation binding, bounded work and memory, state diagnostics without secrets, and hostile-media handling are testable decisions. Obscurity may remain an additional analysis cost. It should not bear the product’s entire security burden, especially when tools make correlating the implementation easier.

Appendix B: bench, versions, and reproduction#

Data table 03
ComponentReference for this article
Examined application profileAndroid 3.1.1, build 100315
libavcPublic source; revision recorded in laboratory records
libxaacPublic source; revision recorded in laboratory records
Confirmation before the Core playermacOS ARM64; audible audio and camera B video
Explorer inputsSynthetic; do not reproduce an authorizable session

The bench combined installed-APK analysis, native inspection, metadata instrumentation, offline parser tests, and independent execution. Internal laboratory documentation records SDK behavior, decoder boundaries, native builds, and application flows. Exact revisions remain in those audit records; the public article describes builds and results without requiring access to our private repository.

Readers can exercise local page controls or inspect tables. The probe transformation XORs each byte with 0xFF; applying it twice recovers the example. Reassembly ACK sequences are normal 4,8,12, reordered 0,8,12, and loss 4,4,4,12. A pending-fragment conflict rejects terminally without releasing those bytes; consumed duplicates are discarded without comparing their contents. These checks validate the educational model, not the complete SDK transport implementation.

Development checks ensure fields cover every example byte exactly, body length agrees with content, and reset removes prior simulation state. The page has no fetch, WebSocket, account storage, or login form. Its modules are local and loaded only here. Browser review with and without JavaScript matters because making technical content depend on animation would add an unnecessary barrier to understanding a protocol.

A session with a real camera requires an authorized account and device, compatible inputs, and service acceptance. Credentials, private URLs, APKs, and raw captures are outside this publication. Invented examples do not fill that gap: they explain layouts, while laboratory records support execution observations. Keeping these roles separate preserves the meaning of reproducibility rather than offering an illustrative packet as an operational session.

Appendix C: white label, platform, and hardware authorship#

The analysis identifies a Dahua/Lechange/Imou software lineage; by itself, it does not identify who manufactured the iM3B-C circuit board.

“White label” describes a possible arrangement in which a company markets an experience under its brand while using components from another. Here the layers differ. Intelbras is the brand and provider of the user-facing Mibo Smart experience; the examined Android package is br.com.intelbras.mibocam, and the official page presents the app as an Intelbras platform. In the installed APK, however, the com.mm.android.easy4ip.Easy4ipApplication entry point, Lechange modules, LCSDK, CommonSDK/netsdk libraries, and OEM configuration show that substantial account, P2P, and media components belong to a technology family preceding the Mibo interface. “OEM configuration” is the name and function observed in code, not a recovered commercial agreement.

There is a public link for that family: Dahua introduced Easy4ip Cloud as its monitoring and P2P service, introduced Lechange as a consumer brand, and later renamed that brand Imou. That helps interpret names in the APK and hostnames. It is an inference about platform lineage, supported by the examined binary and Dahua’s own publications; it does not show that Mibo is an unmodified Imou APK or who operates each server today.

The camera’s physical manufacturer requires different evidence: a label and regulatory filing for the examined unit, model-specific manufacturing documentation, or a verifiable hardware/firmware match. The iM3B-C retail identifier, DAHUAKEY string, Lechange classes, DHAV format, and Easy4ip domains indicate software components and compatibility; none declares who designed or assembled the board, sensor, or full firmware. The available evidence does not establish that Dahua manufactured the iM3B-C. Symbols also cannot establish licensing, support, or contractual responsibility between Intelbras and Dahua.

This distinction matters to Noctua’s engineering. The brand defines the user’s account, product, and support relationship; the observed stack defines the bytes our client must understand; the still-unattributed physical manufacturer would matter for firmware variants and compatibility. Our SDK reproduces only the measured authorized path and does not embed proprietary APK libraries. A camera with the same brand or a library with the same name may require a different flow.

Appendix D: other researchers and the limits of firmware flashing#

Useful references that do not replace this camera’s measurements#

dh-p2p and dahua-p2p implement P2P tunnels in the Dahua family. They helped frame questions and compare structures; as part 1 shows, the PTCP header measured in Mibo is not interchangeable with those projects’ published layout. rroller’s Dahua integration for Home Assistant describes a related practical boundary: a cloud login is not a device-local account, and some Imou/Lechange models expose no local CGI API. That does not establish whether our iM3B-C exposes CGI or supports ONVIF.

An independent teardown of the Intelbras MCA 1002 hub found IMOU_* symbols and an Easy4ip domain in the hub’s firmware. It reinforces that this technology family appears in another Intelbras product, but the MCA 1002 is not an iM3B-C camera: manufacturer, SoC, flash layout, and firmware support do not transfer between them. Those authors deserve credit for what they measured; our APK and Mibo-session attribution comes from our own baseline.

Flashing is not a demonstrated product option#

Intelbras documents firmware updates through its app and publishes a card-based procedure for some iM models with a C suffix. Neither source establishes that the studied iM3B-C accepts arbitrary images, downgrade, or alternative firmware. BotoX’s Dahua Firmware Mod Kit extracts and repacks images for other Dahua cameras; its stated test used a different model. Sharing a software family does not make an image compatible with this board.

OpenIPC’s stock-firmware backup guide explains why a complete backup before any write matters: bootloader, calibration, device identity, and credentials may live in flash. A raw dump may also expose Wi-Fi and P2P keys, so it is not a public attachment. We did no dump, flash, downgrade, or persistent camera modification in this research. Any future firmware study of an owned, isolated unit would start with verified physical identification, private preservation of the original state, and a tested recovery path. Noctua’s recording plan does not depend on promising such a flash.

References#


  1. The laboratories use synthetic values to preserve private identities and media. They neither reproduce an actual session nor send packets. The second is an educational offset/reassembly model, not the complete PTCP state machine. ↩︎ ↩︎