GP-5411 Added plt thunk patterns, pre-fill of edit thunk GUI, loosened

thunk address calculation for more complicated thunks
This commit is contained in:
emteere 2025-03-03 22:37:45 +00:00
parent c2533aaf6e
commit 208f6f3ed1
4 changed files with 127 additions and 33 deletions

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,6 +15,8 @@
*/ */
package ghidra.app.cmd.function; package ghidra.app.cmd.function;
import static ghidra.program.model.symbol.RefType.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -732,36 +734,66 @@ public class CreateThunkFunctionCmd extends BackgroundCommand<Program> {
FlowType flowType, boolean checkForSideEffects, HashSet<Varnode> setRegisters, FlowType flowType, boolean checkForSideEffects, HashSet<Varnode> setRegisters,
HashSet<Varnode> usedRegisters) { HashSet<Varnode> usedRegisters) {
// conditional jumps can't be thunks. // conditional jumps/call terminators can't be thunks,
// any other flow, not good // unless not checkingForSideEffects and just trying to get possible thunk address.
Address flowingAddr = null;
if ((flowType.isJump() || flowType.equals(RefType.COMPUTED_CALL_TERMINATOR) || boolean isJump = flowType.isJump();
flowType.equals(RefType.CALL_TERMINATOR)) && !flowType.isConditional()) { boolean isCall = flowType.isCall();
// program counter should be assumed to be used boolean isConditional = flowType.isConditional();
// assume PC is used when considering registers that have been set // only jump/call are allowed
Register PC = program.getLanguage().getProgramCounter(); if (!(isJump || isCall)) {
if (PC != null) { return null;
usedRegisters.add(new Varnode(PC.getAddress(), PC.getMinimumByteSize())); }
// no conditional jumps allowed
if (isJump && isConditional) {
return null;
}
if (isCall) {
// Any conditional call considered as having side-effects
if (isConditional && checkForSideEffects) {
return null;
} }
setRegisters.removeAll(usedRegisters); // CALL_TERMINATOR, COMPUTED_CALL_TERMINATOR
else if (!flowType.isTerminal()) {
// check that the setRegisters are all hidden, meaning don't care. if (flowType == COMPUTED_CALL ||
for (Iterator<Varnode> iterator = setRegisters.iterator(); iterator.hasNext();) { flowType == UNCONDITIONAL_CALL) {
Varnode rvnode = iterator.next(); // consider any simple call having side-effects
Register reg = program.getRegister(rvnode); if (checkForSideEffects) {
// the register pcode access could have fallen in the middle of a valid register return null;
// thus no register will exist at the varnode }
if (reg != null && reg.isHidden()) {
iterator.remove();
} }
} }
}
// program counter should be assumed to be used
// assume PC is used when considering registers that have been set
Register PC = program.getLanguage().getProgramCounter();
if (PC != null) {
usedRegisters.add(new Varnode(PC.getAddress(), PC.getMinimumByteSize()));
}
setRegisters.removeAll(usedRegisters);
// if not checking for sideEffect registers set, or there are no side-effects // check that the setRegisters are all hidden, meaning don't care.
if (!checkForSideEffects || setRegisters.size() == 0) { for (Iterator<Varnode> iterator = setRegisters.iterator(); iterator.hasNext();) {
flowingAddr = getFlowingAddress(program, instr); Varnode rvnode = iterator.next();
Register reg = program.getRegister(rvnode);
// the register pcode access could have fallen in the middle of a valid register
// thus no register will exist at the varnode
if (reg != null && reg.isHidden()) {
iterator.remove();
} }
} }
Address flowingAddr = null;
// if not checking for sideEffect registers set, or there are no side-effects
if (!checkForSideEffects || setRegisters.size() == 0) {
flowingAddr = getFlowingAddress(program, instr);
}
return flowingAddr; return flowingAddr;
} }

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -72,22 +72,30 @@ class EditThunkFunctionAction extends ProgramContextAction {
if (func == null) { if (func == null) {
return; return;
} }
Address funcEntry = func.getEntryPoint();
Function refFunc = func.getThunkedFunction(false); Function refFunc = func.getThunkedFunction(false);
if (refFunc == null) {
// if not already thunked, fill in a possible value from functions instructions
Address thunkAddr = CreateThunkFunctionCmd.getThunkedAddr(program, funcEntry, false);
if (thunkAddr != null) {
refFunc = functionMgr.getFunctionAt(thunkAddr);
}
}
Symbol refSymbol = (refFunc == null ? null : refFunc.getSymbol());
// Prompt for function referenced by thunk // Prompt for function referenced by thunk
ThunkReferenceAddressDialog dialog = new ThunkReferenceAddressDialog(funcPlugin.getTool()); ThunkReferenceAddressDialog dialog = new ThunkReferenceAddressDialog(funcPlugin.getTool());
dialog.showDialog(program, func.getEntryPoint(), dialog.showDialog(program, funcEntry, refSymbol);
refFunc != null ? refFunc.getSymbol() : null);
Symbol referencedSymbol = dialog.getSymbol(); Symbol referencedSymbol = dialog.getSymbol();
Address referencedFunctionAddr = dialog.getAddress(); Address referencedFunctionAddr = dialog.getAddress();
CreateThunkFunctionCmd cmd; CreateThunkFunctionCmd cmd;
if (referencedSymbol != null) { if (referencedSymbol != null) {
cmd = new CreateThunkFunctionCmd(func.getEntryPoint(), null, referencedSymbol); cmd = new CreateThunkFunctionCmd(funcEntry, null, referencedSymbol);
} }
else if (referencedFunctionAddr != null) { else if (referencedFunctionAddr != null) {
cmd = new CreateThunkFunctionCmd(func.getEntryPoint(), null, referencedFunctionAddr); cmd = new CreateThunkFunctionCmd(funcEntry, null, referencedFunctionAddr);
} }
else { else {
return; // cancelled return; // cancelled

View file

@ -36,4 +36,31 @@
<possiblefuncstart validcode="function" label="__get_pc_thunk_lr" /> <!-- must be a function here --> <possiblefuncstart validcode="function" label="__get_pc_thunk_lr" /> <!-- must be a function here -->
</pattern> </pattern>
<pattern> <!-- .plt entry thunk -->
<data>
0xf8410028 <!-- std r2,0x28(r1) -->
001111.. ...00010 0xff 0xff <!-- subis rX,r2,0x1 -->
0xe9 ........ ........ ........ <!-- ld rX,#(x) -->
0x7d 0x.9 0x03 0xa6 <!-- mtctr rX -->
0xe8 010..... ........ ........ <!-- ld r2,#(x) -->
0x28220000 <!-- cmpldi r2,0 -->
0x4c 1..00010 0x04 0x20 <!-- bnectr+ -->
010010.. ........ ........ ......00 <!-- b X@plt -->
</data>
<funcstart after="defined" thunk="true"/> <!-- must be something define before this -->
</pattern>
<pattern> <!-- .plt entry thunk -->
<data>
0xf8410028 <!-- std r2,0x28(r1) -->
0xe9 ........ ........ ........ <!-- ld rX,#(x) -->
0x7d 0x.9 0x03 0xa6 <!-- mtctr rX -->
0xe8 010..... ........ ........ <!-- ld r2,#(x) -->
0x28220000 <!-- cmpldi r2,0 -->
0x4c 1..00010 0x04 0x20 <!-- bnectr+ -->
010010.. ........ ........ ......00 <!-- b X@plt -->
</data>
<funcstart after="defined" thunk="true"/> <!-- must be something define before this -->
</pattern>
</patternlist> </patternlist>

View file

@ -35,5 +35,32 @@
<data>0x21 0x00 0x80 0x4e</data> <!-- blrl --> <data>0x21 0x00 0x80 0x4e</data> <!-- blrl -->
<possiblefuncstart validcode="function" label="__get_pc_thunk_lr" /> <!-- must be a function here --> <possiblefuncstart validcode="function" label="__get_pc_thunk_lr" /> <!-- must be a function here -->
</pattern> </pattern>
<pattern> <!-- .plt entry thunk -->
<data>
0x280041f8 <!-- std r2,0x28(r1) -->
0xff 0xff ...00010 001111.. <!-- subis rX,r2,0x1 -->
........ ........ ........ 0xe9 <!-- ld rX,#(x) -->
0xa6 0x03 0x.9 0x7d <!-- mtctr rX -->
........ ........ 010..... 0xe8 <!-- ld r2,#(x) -->
0x00002228 <!-- cmpldi r2,0 -->
0x20 0x04 1..00010 0x4c <!-- bnectr+ -->
......00 ........ ........ 010010.. <!-- b X@plt -->
</data>
<funcstart after="defined" thunk="true"/> <!-- must be something define before this -->
</pattern>
<pattern> <!-- .plt entry thunk -->
<data>
0x280041f8 <!-- std r2,0x28(r1) -->
........ ........ ........ 0xe9 <!-- ld rX,#(x) -->
0xa6 0x03 0x.9 0x7d <!-- mtctr rX -->
........ ........ 010..... 0xe8 <!-- ld r2,#(x) -->
0x00002228 <!-- cmpldi r2,0 -->
0x20 0x04 1..00010 0x4c <!-- bnectr+ -->
......00 ........ ........ 010010.. <!-- b X@plt -->
</data>
<funcstart after="defined" thunk="true"/> <!-- must be something define before this -->
</pattern>
</patternlist> </patternlist>