Binder token

Một đặc tính độc đáo của Binder là mỗi instance sẽ duy trì một id duy nhất giữa các process trong hệ thống.

Tính tiện lợi này được cung cấp bởi Binder kernel driver.  Binder kernel driver sẽ phân tích nội dung của mỗi Binder transaction và gán cho mỗi Binder object một số nguyên 32-bit duy nhất.

Để chắc chắn == operator tuân thủ tính duy nhất của Binder, reference tới Binder sẽ được xử lý khác so với các reference của các object bình thường. Đặc biệt hơn, mỗi reference của một Binder object sẽ được gán một trong hai:

  1. Một địa chỉ bộ nhớ ảo trỏ tới một Binder object ở trong cùng process, hoặc
  2. Một số id 32-bit độc nhất (được gán bởi Binder kernel driver) trỏ tới địa chỉ bộ nhớ ảo của của Binder ở một process khác.

Binder kernel driver duy trì mapping của địa chỉ bộ nhớ địa phương tới Binder handle từ xa (và ngược lại) chỗ mỗi Binder nó nhìn thấy, và gán mỗi reference của object Binder tới giá trị thích hợp mà đảm bảo tính bình đẳng sẽ như expected trong remote process

Tính độc nhất của Binder object được sử dụng với mục đích đặc biệt: để chia sẻ  và là security access token. Binder là duy nhất (globally), có nghĩa rằng bạn có thể tạo ra một Binder object và không một ai khác có thể tạo ra một Binder object khác giống với Binder object bạn đã tạo (giống với ở đây được hiểu là sử dụng toán tử ==)

Vì lý do này, Binder được sử dụng rộng rãi trong application framework để đảm bảo tính bảo mật trong sự tương tác giữa các process: Client có thể tạo Binder object để sử dụng như token có thể chia sẻ với server process, và server có thể sử dụng chính nó để xác nhận yêu cầu của client mà không có bất cứ cách nào khác để người khác có thể giả mạo nó.

Dưới đây là một ví dụ khi PowerManager acquire và sau đó release một wake lock:

public class MyActivity extends Activity {

    private PowerManager.WakeLock wakeLock;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);

        wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, “My Tag”);
        wakeLock.acquire();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        wakeLock.release();
    }
}

public final class PowerManager {
    private final IpowerManager mService 

    public WakeLock newWakeLock(int levelAndFlags, String tag) {
      return new WakeLock(levelAndFlags, tag);
    }

    public final class WakeLock {
        private final IBinder mToken;
        private final int mFlags;
        private final String mTag;

        WakeLock(int flags, String tag) {
            mToken = new Binder();
            mFlags = flags;
            mTag = tag;
        }

        public void acquire() {
            mService.acquireWakeLock(mToken, mFlags, mTag);
        }

        public void release() {
            mService.releaseWakeLock(mToken);
        }
    }
}

Vậy, việc gì xảy ra ở đây vậy?

  1. Một ứng dụng client yêu cầu một instance của PowerManager ở trong onCreate(). PowerManager cung cấp một interface cho ứng dụng client để nói chuyện với PowerManagerService, PowerManagerService được chạy trong System Server process và có trách nhiệm quản lý power state của thiết bị.
  2. Ứng dụng client tạo và chiếm lấy wake lock trong onCreate(). PowerManager gửi Binder token của instance của wakelock cho PowerManagerService. Khi PowerManagerService nhận được yêu cầu, nó giữ cho thiết bị không bị tắt màn hình cho đến khi…
  3. Ứng dụng client nhả wake lock ở trong onDestroy(). PowerManager gửi Binder token của wake lock cho PowerManagerService. Khi PowerManagerService nhận được yêu cầu, nó so sánh token nhận được với các token được lưu lại, và chỉ nhả wake lock ra nếu hai Binder token là giống nhau.

Bước xác nhận này là một trong những bước bảo mật quan trong để đảm bảo rằng ứng dụng sẽ không lừa PowerManagerService nhả wake lock đang bị giữ bởi một ứng dụng khác.

Bởi tính năng duy nhất, Binder token được sử dụng rộng rãi trong hệ thống để đảm bảo bảo mật. Thú vị nhất là ở cách Android framework sử dụng Window token.

  1. Window Tokens

Đã bao giờ bạn gặp hàm getWindowToken() của class View? Giống như tên của nó, window token là một dạng đặc biệt của Binder token mà WindowManager sử dụng để xác định tính duy nhất của các window trong hệ thống.

Window tokens cực kỳ quan trọng trong bảo mật bởi nó không cho phép các ứng dụng độc hại vẽ lên trên window của một ứng dụng khác. Window manager sẽ ngăn chặn việc này bằng cách yêu cầu ứng dụng truyền lên window token của ứng dụng đó mỗi khi add hoặc remove window.

Nếu token không giống nhau, window manager sẽ loại bỏ yêu cầu và ném ra BadTokenException. Nếu không có window token, window manager sẽ không thể bảo vệ chính nó khỏi các ứng dụng độc hại.

Tới thời điểm này, bạn có thể hỏi những tình huống cần lấy window token. Đây là một vài ví dụ:

  1. Khi một ứng dụng khởi động lần đầu tiền, ActivityManagerService tạo một loại window token đặc biệt, được gọi là application window token, nó sẽ identify top-level container window. ActivityManagerService sẽ đưa token này cho cả ứng dụng và cả window manager, và ứng dụng gử token cho window manager mỗi khi nó muốn thêm hoặc bớt một window tới màn hình. Điều này đảm bảo tính bảo mật giữa tương tác giữa ứng dụng và window manager (bằng cách làm cho nó không thể thêm window vào bên trên của một ứng dụng khác), và cũng làm cho activity manager dễ dàng gửi các yêu cầu trực tiếp tới window manager. Ví dụ , activity manager có thể “ẩn tất cả window của token này”, và window manager sẽ có thể xác định một danh sách các windows cần được ẩn.

– Ứng dụng có thể yêu cầu InputMethodManager ẩn bàn phím ảo bằng cách gọi tới hàm hideSoftInputFromWindow(IBinder windowToken, int flags), nhưng cần phải truyền vào window token. Nếu Window token không match với window hiện tại đang nhận input, InputMethodManager sẽ từ chối request đó. Điều này để chắc chắn rằng ứng dụng độc hại sẽ không thể force-close bàn phím ảo được bật lên bởi một ứng dụng khác.

– Ứng dụng mà add window mới vào màn hình (sử dụng addView(View, WindowManager.LayoutParams) có thể cần phải truyền cả window token của application bằng cách set vào trường WindowManager.LayoutParams.token. Không phải ứng dụng nào cũng phải làm việc này bởi getWindowManager() sẽ trả về WindowManager mà đã set token đó cho bạn. Cần phải nói thêm rằng, nếu trong tương lai bạn gặp phỉ tình trạng mà bạn phải add một panel window tới screen từ background service, bạn cần biết rằng bạn sẽ phải sign request thủ công với window token của application của bạn để làm được điều đó.

Kết luận:

Mặc dù hầu hết các lập trình viên không biết tới sự tồn tại của Binder, Binder token được sử dụng vô cùng rộng tãi trong hệ thống để đảm bảo tính bảo mật. Binder token là trái tim của Android framework, và nếu không có chúng, việc bảo mật giao tiếp giữa các process sẽ khó có thể thực hiện được.

Leave a Reply

Your email address will not be published. Required fields are marked *