The issue

As most of you already know, there is an issue in the Homebrew Channel that makes crash homebrew apps when you don't have your wii connected to the network and HBC is configured to load apps without reloading IOS, which is the only known way to get AHBPROT access rights.
Since 1.0.7, HBC launches an asynchronous call to the Wii in order to detect the network.
And usually the IOS58 take a long time to respond if no network connection is detected.
The problem is that the HBC doesn't shutdown communication with the internal network hardware on IOS side, in other words it doesn't cancel the asynchronous call before launching a homebrew.
So the IPC reply for that async call comes when the app is already loaded, causing the crash.
This issue affects ALL homebrew loaded by HBC 1.0.7+.


The known workarounds

There are 2 known workarounds to fix this issue but both have drawbacks.
The 1st way is to force HBC to reload IOS when it loads the app, which also kill any initiated network callback automatically and prevents the apps from crashing.
However this solution makes you loose AHBPROT access rights.
Another solution is to wait about 1 minute on the HBC hoping that the asynchronous call is terminated in the meanwhile.
However experience teaches that this workaround doesn't work always for every Wii out there.

Finding a solution

This old article by Bushing is really enlightening and gave me enough information to start investigating the problem.
Syscall 0x54, AKA set_ahbprot, is in charge of enabling/disabling AHBPROT access, see IOS/Syscalls - WiiBrew By disassembling and inspecting the kernel (FFS, ES and IOSP modules) you find out that syscall 0x54 is called only twice and both invocations are from the same function, which is called in turn by the LAUNCH_TITLE ioctl.
This function checks access rights from the TMD of the title to be launched, i.e the 4 bytes at offset 0x1D8. See Title metadata - WiiBrew

Here is a quick C translation of the interesting part of this function:

Code:
void check_access_rights(void *tmd)
{
  ...
  s32 access_rights = *(s32 *)(tmd+0x1D8); 

  /* Shift bit 0 to the sign bit and check it */
  if ((access_rights << 31) < 0) {
    /* Bit 0 is ON so enable AHBPROT */
    set_ahbprot(1);
  }
  else {
    /* Bit 0 is OFF so disable AHBPROT */
    set_ahbprot(0);
  }
  ...
}
So the idea behind the fix is to patch this function in order to emulate AHBPROT access rights from the TMD without having to modify the TMD itself, which of course would break the signature.
After doing that, we have just to reload the IOS to automatically terminate the asynchronous call mentioned above.
Of course to patch the running ios we need AHBPROT access rights, but this is not a problem since it is provided by HBC.

With this in mind the patch is trivial. We have just to enable AHBPROT, i.e. by calling set_ahbprot(1), regardless of bit 0 in the access rights field.
So we want that previous code becomes something like that:

Code:
void check_access_rights(void *tmd)
{
  ...
  s32 access_rights = *(s32 *)(tmd+0x1D8); 

  /* Shift bit 0 to the sign bit and check it */
  if ((access_rights << 31) < 0) {
    /* Bit 0 is ON so enable AHBPROT */
    set_ahbprot(1);
  }
  else {
    /* Bit 0 is OFF but we still enable AHBPROT */
    set_ahbprot(1);
  }
  ...
}

The fix

Finally let's see the code to search for the binary pattern, patch the right byte in ES and then reload the IOS.
That's all you need to fix the connection issue.

Code:
#define HAVE_AHBPROT ((*(vu32*)0xcd800064 == 0xFFFFFFFF) ? 1 : 0)
#define MEM_REG_BASE 0xd8b4000
#define MEM_PROT (MEM_REG_BASE + 0x20a)

static void disable_memory_protection() {
    write32(MEM_PROT, read32(MEM_PROT) & 0x0000FFFF);
}

static u32 apply_patch(char *name, const u8 *pattern, u32 pattern_size, const u8 *patch, u32 patch_size, u32 patch_offset) {
    u8 *ptr_start = (u8*)*((u32*)0x80003134), *ptr_end = (u8*)0x94000000;
    u32 found = 0;
    u8 *location = NULL;
    while (ptr_start < (ptr_end - patch_size)) {
        if (!memcmp(ptr_start, pattern, pattern_size)) {
            found++;
            location = ptr_start + patch_offset;
            u8 *start = location;
            u32 i;
            for (i = 0; i < patch_size; i++) {
                *location++ = patch[i];
            }
            DCFlushRange((u8 *)(((u32)start) >> 5 << 5), (patch_size >> 5 << 5) + 64);
            ICInvalidateRange((u8 *)(((u32)start) >> 5 << 5), (patch_size >> 5 << 5) + 64);
        }
        ptr_start++;
    }
    return found;
}

const u8 es_set_ahbprot_pattern[] = { 0x68, 0x5B, 0x22, 0xEC, 0x00, 0x52, 0x18, 0x9B, 0x68, 0x1B, 0x46, 0x98, 0x07, 0xDB };
const u8 es_set_ahbprot_patch[]   = { 0x01 };

u32 IOSPATCH_AHBPROT() {
    if (HAVE_AHBPROT) {
        disable_memory_protection();
        return apply_patch("es_set_ahbprot", es_set_ahbprot_pattern, sizeof(es_set_ahbprot_pattern), es_set_ahbprot_patch, sizeof(es_set_ahbprot_patch), 25);
    }
    return 0;
}

int main(int argc, char* argv[])
{
    int ret;
  
    /* Enable AHBPROT on title launch */
    ret = IOSPATCH_AHBPROT();
    if (ret) {
        /* Reload current IOS, typically IOS58 */
        IOS_ReloadIOS(IOS_GetVersion());
    }
    else {
        /*
         * Fatal error!!!
         * Unable to patch the running IOS.
         * The application has been likely launched without AHBPROT access rights. 
         */
    }

    /*
     * Put your code here
     */
}
Notes

This approach has been intensively tested on a modified version of IOS236 Installer v5 and it never failed.
Notice that when launching the original IOS236 Installer v5 I get always a crash before completing the procedure.
The apply_patch function above is intended to be generic, so it's not optimized and a large amount of memory is compared to match the binary pattern.
You might want to narrow down this memory range, however the function is fast enough to avoid problems.
Through the same approach described in this topic we can patch bit 1 of the access rights field in TMD in order to enable DVD video mode.
If you're interested in this patch I'll release it too.

Source- gbatemp