Categories
Tutorial

How to reinstall macOS after hard drive is wiped

In case you wiped your Mac’s hard disk or erased it’s partition map (like me), don’t panic.

The bad news is if you don’t have your data backed up before, you probably lost them all already (Unless you pay professional data recovery company to help you salvage them). However, the good news is, you can have a fresh macOS on your machine soon.

1. Download the macOS installer

If you search online, you will probably find a bunch of guides that tell you to grab the full macOS installer from AppStore. This only works if you have a working Mac that still run a older version of the macOS version you want to install. For example, if you want to install High Sierra (10.13), you need a mac that’s running on Sierra (10.12). Which I wasn’t, and probably you aren’t.

So to get the macOS installer, your best chance is to torrent it from some torrent site. Of course, depending on where you live and the laws, it might be illegal to do so. So I can’t help you more here but it shouldn’t be hard if you have the right keywords to search.

2. Create bootable installer for macOS

You can follow this official guide from apple, it’s very informative and covers all the cases: https://support.apple.com/en-ca/HT201372

3. Mounting the missing hard disk

With all those task done, plug in your bootable USB into your Mac and power it up. Your mac should load the recovery utility in the USB without any keyboard press because your hard disk is wiped.

In the recovery utility, select Disk Utility. You want to make sure your disk0 which is your hard disk is listed. It’s should be at least 500GB for most iMac. It’s definitely not the 32 GB or even smaller drive, that’s your bootable installer USB. If you can see it in Disk Utility, go to the next step. If not, read on.

If it is not visible, which is possible because we wiped it, you need to fire up Terminal. You can do so by going to the menu bar at the top of the screen > Utilities > Terminal. Then type the command:

diskutil list

If you can see disk0 listed in the output, you can mount it by entering the following command:

diskutil mountDisk 0

This should do the job and you can verify by quitting terminal and go back to DiskUtility.

If you cannot mount the drive or the drive is simply not listed in diskutil list, then you probably have a hardware issue and hardware replacement is needed.

4. Erase the hard disk

Since we are doing a fresh installation, we need to erase the disk and start new. Yes, you will lose 100% of your data after this step.

You can do this using Disk Utility. Make double, triple sure that you selected your disk0 on the left sidebar. Click “Erase” at the top. Name it “Macintosh HD” and select APFS for the format option. Finally click Erase and this will reformat the hard disk.

You can quit the Disk Utility app after it’s done formatting.

5. Install macOS

Click Install macOS in the recovery menu.

Depending on how you got your macOS installation image, you might get the error “This copy on the Install macOS High Sierra.app application is damaged and can’t be used to install macOS” or similar.

This is because there is a certificate/signature of Apple contained inside the installer, and it is out of date compare to our Mac’s date.

We can by pass this by firing up Terminal once again, then type the following:

date 121501012018

The format of the date [mm][dd][HH][MM][yyyy]. This will overwrite the date of your machine.

Depending on the installer you are using, the date above may not work. If this happens, just change the date again and try the “Install macOS” option in the recovery menu again.

If you successfully get to the installation location selection screen, make sure you have the newly formatted hard disk selected, not the USB.

Then just follow the installer instruction to finish your installation.

Good luck and enjoy your new Mac!

Categories
Tutorial

React Native URL Scheme and Linking API

React Native is possible the best hybrid framework for building mobile app at the moment. Not just its performance but also the development experience it provides. If you build it well, one really can’t tell if your app is native or a hybrid.

I spent my last 3 month developing a complicated hybrid app using react native. It involves many network requests, forms, lists, etc. It was undoubtedly challenging, but the result was quite satisfying.

For my next project, I was given another chance to work with React Native. This time I need to build a “AppStore” kind of application, which allow the user to install apps and launch installed apps right from this custom “AppStore”.

To launch other Apps in iOS or Android, we must register custom URL Schemes in the Apps we wish to launch.

Register URL Schemes

iOS

On iOS, we need to add a key CFBundleURLTYpes to the Info.plist file within the Xcode project.

Under this key we add an array, and define 2 keys CFBundleURLName and CFBundleURLSchemes.

Under CFBundleURLName you need to add a unique string that identifies your bundle, I used the same string as my app’s bundle identifier and it worked fine. Under CFBundleURLSchemes you need to add another array, and put in your desired URL schemes.

Here is an example of such entry inside Info.plist:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>com.johnsonsu.example</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>johnsonsu-example</string>
        </array>
    </dict>
</array>

More info about iOS URL Scheme can be found on the official documentation.

Android

Android’s setup is similar to iOS. We need to add a custom intent-filter in the Android app’s AndroidManifest.xml file.

Here is an example of such intent-filter:

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <data android:scheme="johnsonsu-example"/>
</intent-filter>

Since React Native’s Linking API will be creating an intent with action equals to android.intent.action.VIEW, your intent-filter must contain an action tag with this name.

The data tag is where you should define your custom URL Scheme. In this example I used the same one as the iOS scheme. Doing it this way allows me to use the exact same code in React Native to launch these applications in both iOS and Android.

More info about Android Intent Filter can be found on the official documentation.

Launching the App in React Native

If you setup your apps properly, you should be able to launch that app in React Native using:

import { Linking } from 'react-native';

Linking.openURL('johnsonsu-example://').catch(err => console.error('An error occurred', err));

If the OS can’t handle the URL you tried to open, the exception will be caught inside the catch block, and you should handle it properly to prevent a crash.

A safer way to open URL is to check if the OS can handle it before you open it. You can check it using:

import { Linking } from 'react-native';

const url = 'johnsonsu-example://';
Linking.canOpenURL(url).then(supported => {
  if (!supported) {
    console.log('Can\'t handle url: ' + url);
  } else {
    return Linking.openURL(url);
  }
}).catch(err => console.error('An error occurred', err));

This works on Android without any setup. However, on iOS 9 or above, you need to add LSApplicationQueriesSchemes to your React Native app’s Info.plist or Linking.canOpenURL(url) will always returns false.

This is an example entry in Info.plist:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>johnsonsu-example</string>
    <string>youtube</string>
    <string>facebook</string>
</array>

There you have it, a simple way to launch any other apps from your React Native app!

Categories
Tutorial

WordPress xmlrpc.php attack

Recently, one of my WordPress website has been attacked by thousands of request to the xmlrpc.php file. The attacks came from multiple ip address. Here is my apache access log(/var/apache2/access.log):

62.141.35.242 - - [15/May/2016:21:05:38 +0800] "POST /xmlrpc.php HTTP/1.1" 200 620 "-" "Googlebot/2.1 (+http://www.google.com/bot.html)"
188.120.41.8 - - [15/May/2016:21:05:44 +0800] "POST /xmlrpc.php HTTP/1.1" 200 620 "-" "Googlebot/2.1 (+http://www.google.com/bot.html)"
62.141.35.242 - - [15/May/2016:21:05:39 +0800] "POST /xmlrpc.php HTTP/1.1" 200 620 "-" "Googlebot/2.1 (+http://www.google.com/bot.html)"
188.120.41.8 - - [15/May/2016:21:05:54 +0800] "POST /xmlrpc.php HTTP/1.1" 200 0 "-" "Googlebot/2.1 (+http://www.google.com/bot.html)"
188.120.41.8 - - [15/May/2016:21:05:35 +0800] "POST /xmlrpc.php HTTP/1.1" 200 620 "-" "Googlebot/2.1 (+http://www.google.com/bot.html)"
62.141.35.242 - - [15/May/2016:21:05:39 +0800] "POST /xmlrpc.php HTTP/1.1" 200 620 "-" "Googlebot/2.1 (+http://www.google.com/bot.html)"
62.141.35.242 - - [15/May/2016:21:05:41 +0800] "POST /xmlrpc.php HTTP/1.1" 200 620 "-" "Googlebot/2.1 (+http://www.google.com/bot.html)"
62.141.35.242 - - [15/May/2016:21:05:37 +0800] "POST /xmlrpc.php HTTP/1.1" 200 620 "-" "Googlebot/2.1 (+http://www.google.com/bot.html)"
62.141.35.242 - - [15/May/2016:21:05:39 +0800] "POST /xmlrpc.php HTTP/1.1" 200 620 "-" "Googlebot/2.1 (+http://www.google.com/bot.html)"
62.141.35.242 - - [15/May/2016:21:05:43 +0800] "POST /xmlrpc.php HTTP/1.1" 200 620 "-" "Googlebot/2.1 (+http://www.google.com/bot.html)"

These attacks come and go, and they had took down my site several times over the last few months. I did some google and found out that this kind of attack has been around for a while. It attempts to use the xmlrpc.php file to brute force WordPress logins.

Solution

Block IPs with ufw

Most ubuntu server has ufw installed, it can be used to block specific ip address from accessing the server.

I run the following command(reference) to get a list of attacker’s ip addresses:

$ grep xmlrpc /var/log/apache2/access.log | cut -d' ' -f1 | sort | uniq -c | sort -rn | head

// results
6039 62.141.35.242
1566 154.16.63.40
1411 188.120.41.8
 248 195.2.252.132

Those numbers in front of IPs are the number of times each ip requested the xmlrpc.php file.

Then for each of the IP address, I used the following command to block them using ufw:

sudo ufw deny from 62.141.35.242 // replace with your attacker's ip address

I then ran the list attacker command a few more times after I blocked all IPs. Unfortunately, the access count is still increasing. There seems to be a problem with iptables on Ubuntu, but I couldn’t find a solution. So I tried another method to deal with these attacks.

Modifying Apache Virtual Host Config

Which is adding the following:

<VirtualHost>
…    
    <files xmlrpc.php>
      order allow,deny
      deny from all
    </files>
</VirtualHost>

to your WordPress apache virtual host config file.
My config file is the standard

/etc/apache2/sites-available/000-default.conf

Modifying this file will not block the attacker’s request, but reduce the amount of resources the request consumes on your server.

After I reload the apache with

sudo services apache2 restart

I was able to access my WordPress website again.