[Galaxy S 4 Developer Discussion [Developers-Only]] Fenny:
Just for the sake of transparency :D Here is a quick rundown of the process for everyone to see.

High level overview:

1. ) baksmali classes.dex from NfcNci.apk

2. ) Mod the resulting smali.

3. ) smali, and inject back into NfcNci.apk

Since this is the dev section I will focus on step 2 you can read about steps 1 and 3 elsewhere. For the sake of this example I will be working with the system dump from the original release of the gt-i9505g (S4 Google Edition) System Software.

What you are looking for is this function contained in the NfcIntegrityChecker class:

.method public checkIntegrity(Ljava/lang/String;)Z

If we track back the exception error message that comes up in the debug log ("Client module is tampered"), you will see that the text is contained in the NfcService class within the function ".method public enforceNfcIntegrity()V"

.method public enforceNfcIntegrity()V
.locals 3

.prologue
.line 470
iget-object v1, p0, Lcom/android/nfc/NfcService;->mIntegrityChecker:Lcom/android/nfc/NfcIntegrityChecker;

monitor-enter v1

.line 471
:try_start_0
iget-object v0, p0, Lcom/android/nfc/NfcService;->mIntegrityChecker:Lcom/android/nfc/NfcIntegrityChecker;

invoke-virtual {v0}, Lcom/android/nfc/NfcIntegrityChecker;->getStatus()I

move-result v0

const/4 v2, 0x3

if-ne v0, v2, :cond_0

.line 472
new-instance v0, Ljava/lang/SecurityException;

const-string v2, "NfcIntegrityChecker is not on the proper status"

invoke-direct {v0, v2}, Ljava/lang/SecurityException;-><init>(Ljava/lang/String;)V

throw v0

.line 480
:catchall_0
move-exception v0

monitor-exit v1
:try_end_0
.catchall {:try_start_0 .. :try_end_0} :catchall_0

throw v0

.line 475
:cond_0
:try_start_1
iget-object v0, p0, Lcom/android/nfc/NfcService;->mIntegrityChecker:Lcom/android/nfc/NfcIntegrityChecker;

invoke-virtual {v0}, Lcom/android/nfc/NfcIntegrityChecker;->getStatus()I

move-result v0

const/4 v2, 0x2

if-eq v0, v2, :cond_1

iget-object v0, p0, Lcom/android/nfc/NfcService;->mIntegrityChecker:Lcom/android/nfc/NfcIntegrityChecker;

const-string v2, "client"

invoke-virtual {v0, v2}, Lcom/android/nfc/NfcIntegrityChecker;->checkIntegrity(Ljava/lang/String;)Z

move-result v0

if-eqz v0, :cond_1

iget-object v0, p0, Lcom/android/nfc/NfcService;->mIntegrityChecker:Lcom/android/nfc/NfcIntegrityChecker;

const-string v2, "clientlib"

invoke-virtual {v0, v2}, Lcom/android/nfc/NfcIntegrityChecker;->checkIntegrity(Ljava/lang/String;)Z

move-result v0

if-nez v0, :cond_2

.line 478
:cond_1
new-instance v0, Ljava/lang/SecurityException;

const-string v2, "Client module is tampered"

invoke-direct {v0, v2}, Ljava/lang/SecurityException;-><init>(Ljava/lang/String;)V

throw v0

.line 480
:cond_2
monitor-exit v1
:try_end_1
.catchall {:try_start_1 .. :try_end_1} :catchall_0

.line 481
return-void
.end method

If we follow the flow of the application we see that the code for this failure is only reached if the function call to

checkIntegrity(Ljava/lang/String;)Z

returns false, or the status is not properly set. (the 'Z' at the end means it returns a boolean).

There are two ways to handle this. We can either patch out the call to checkIntegrity entirely (jump over it, or literally remove the calls), or we can modify the checkIntegrity function so that it always returns true.

So, let's look at the checkIntegrity function:

.method public checkIntegrity(Ljava/lang/String;)Z
.locals 11
.parameter "type"

.prologue
const/4 v10, 0x2

const/4 v9, 0x1

const/4 v8, 0x0

.line 293
iget-object v5, p0, Lcom/android/nfc/NfcIntegrityChecker;->mModuleMap:Ljava/util/HashMap;

invoke-virtual {v5, p1}, Ljava/util/HashMap;->get(Ljava/lang/Object;)Ljava/lang/Object;

move-result-object v2

check-cast v2, Ljava/util/ArrayList;

.line 294
.local v2, modules:Ljava/util/ArrayList;,"Ljava/util/ArrayList<[Ljava/lang/String;>;"
const/4 v1, 0x0

.line 295
.local v1, module:[Ljava/lang/String;
const/4 v3, 0x1

.line 296
.local v3, ret:Z
invoke-virtual {v2}, Ljava/util/ArrayList;->size()I

move-result v4

.line 298
.local v4, size:I
const/4 v0, 0x0

.local v0, i:I
:goto_0
if-ge v0, v4, :cond_2

.line 299
invoke-virtual {v2, v0}, Ljava/util/ArrayList;->get(I)Ljava/lang/Object;

move-result-object v1

.end local v1 #module:[Ljava/lang/String;
check-cast v1, [Ljava/lang/String;

.line 301
.restart local v1 #module:[Ljava/lang/String;
if-eqz v1, :cond_0

array-length v5, v1

if-eq v5, v10, :cond_4

.line 302
:cond_0
sget-boolean v5, Lcom/android/nfc/NfcIntegrityChecker;->DBG:Z

if-eqz v5, :cond_1

const-string v5, "NfcIntegrityChecker"

const-string v6, "checkIntegrity module format error"

invoke-static {v5, v6}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I

.line 303
:cond_1
const/4 v3, 0x0

.line 314
:cond_2
:goto_1
if-nez v3, :cond_3

iput v10, p0, Lcom/android/nfc/NfcIntegrityChecker;->mStatus:I

.line 316
:cond_3
return v3

.line 307
:cond_4
aget-object v5, v1, v8

aget-object v6, v1, v9

invoke-direct {p0, v5, v6}, Lcom/android/nfc/NfcIntegrityChecker;->checkModuleIntegrity(Ljava/lang/String;Ljava/lang/String;)Z

move-result v5

if-nez v5, :cond_6

.line 308
sget-boolean v5, Lcom/android/nfc/NfcIntegrityChecker;->DBG:Z

if-eqz v5, :cond_5

const-string v5, "NfcIntegrityChecker"

new-instance v6, Ljava/lang/StringBuilder;

invoke-direct {v6}, Ljava/lang/StringBuilder;-><init>()V

const-string v7, "checkModuleIntegrity "

invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v6

aget-object v7, v1, v8

invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v6

const-string v7, " tampered : "

invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v6

aget-object v7, v1, v9

invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v6

invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

move-result-object v6

invoke-static {v5, v6}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I

.line 309
:cond_5
const/4 v3, 0x0

.line 310
goto :goto_1

.line 298
:cond_6
add-int/lit8 v0, v0, 0x1

goto :goto_0
.end method

There is only one return statement, so this should be a simple matter of setting our value, and calling return. In some cases there is code that your application needs to run in order to function, so usually a light touch is a good idea, but in this case all of the code is there to run various checks so we can skip most of it.

The easiest way would be to replace the whole function with this:

.method public checkIntegrity(Ljava/lang/String;)Z
.locals 2
.parameter "type"

.prologue
const/4 v0, 0x1
return v0

.end method

But that method never sat right with me (old school c++ cracking I guess), so I prefer something like this:

add a goto, and modify the constant here:

.line 303

:cond_1

:goto_666

const/4 v3, 0x1

then add the following line to the beginning of the function (after the .prologue)

goto :goto_666

We end up with something like this:

.method public checkIntegrity(Ljava/lang/String;)Z
.locals 11
.parameter "type"

.prologue
const/4 v10, 0x2

const/4 v9, 0x1

const/4 v8, 0x0

goto :goto_666

.line 293
iget-object v5, p0, Lcom/android/nfc/NfcIntegrityChecker;->mModuleMap:Ljava/util/HashMap;

invoke-virtual {v5, p1}, Ljava/util/HashMap;->get(Ljava/lang/Object;)Ljava/lang/Object;

move-result-object v2

check-cast v2, Ljava/util/ArrayList;

.line 294
.local v2, modules:Ljava/util/ArrayList;,"Ljava/util/ArrayList<[Ljava/lang/String;>;"
const/4 v1, 0x0

.line 295
.local v1, module:[Ljava/lang/String;
const/4 v3, 0x1

.line 296
.local v3, ret:Z
invoke-virtual {v2}, Ljava/util/ArrayList;->size()I

move-result v4

.line 298
.local v4, size:I
const/4 v0, 0x0

.local v0, i:I
:goto_0
if-ge v0, v4, :cond_2

.line 299
invoke-virtual {v2, v0}, Ljava/util/ArrayList;->get(I)Ljava/lang/Object;

move-result-object v1

.end local v1 #module:[Ljava/lang/String;
check-cast v1, [Ljava/lang/String;

.line 301
.restart local v1 #module:[Ljava/lang/String;
if-eqz v1, :cond_0

array-length v5, v1

if-eq v5, v10, :cond_4

.line 302
:cond_0
sget-boolean v5, Lcom/android/nfc/NfcIntegrityChecker;->DBG:Z

if-eqz v5, :cond_1

const-string v5, "NfcIntegrityChecker"

const-string v6, "checkIntegrity module format error"

invoke-static {v5, v6}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I

.line 303
:cond_1
:goto_666
const/4 v3, 0x1

.line 314
:cond_2
:goto_1
if-nez v3, :cond_3

iput v10, p0, Lcom/android/nfc/NfcIntegrityChecker;->mStatus:I

.line 316
:cond_3
return v3

.line 307
:cond_4
aget-object v5, v1, v8

aget-object v6, v1, v9

invoke-direct {p0, v5, v6}, Lcom/android/nfc/NfcIntegrityChecker;->checkModuleIntegrity(Ljava/lang/String;Ljava/lang/String;)Z

move-result v5

if-nez v5, :cond_6

.line 308
sget-boolean v5, Lcom/android/nfc/NfcIntegrityChecker;->DBG:Z

if-eqz v5, :cond_5

const-string v5, "NfcIntegrityChecker"

new-instance v6, Ljava/lang/StringBuilder;

invoke-direct {v6}, Ljava/lang/StringBuilder;-><init>()V

const-string v7, "checkModuleIntegrity "

invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v6

aget-object v7, v1, v8

invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v6

const-string v7, " tampered : "

invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v6

aget-object v7, v1, v9

invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v6

invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

move-result-object v6

invoke-static {v5, v6}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I

.line 309
:cond_5
const/4 v3, 0x0

.line 310
goto :goto_1

.line 298
:cond_6
add-int/lit8 v0, v0, 0x1

goto :goto_0
.end method